diff --git a/module_prototyper/README.rst b/module_prototyper/README.rst new file mode 100644 index 000000000..84df73de9 --- /dev/null +++ b/module_prototyper/README.rst @@ -0,0 +1,124 @@ +Module Prototype +================ + +This module allows the administrator to prototype new features and export them as module. +Functional people can prepare the job for a developer who is left with the logic to implement +in addition to everything the prototype does not export yet. + +Installation +============ + +No installation steps required other than installing the module itself. + +Configuration +============= + +No configuration required. + +Usage +===== + +To use this module, you need to: + + * install the dependencies of your future module + * customize your instance by adding fields and creating inherited views + * create your menu items and their window actions + * prepare your data and demo data by creating filters + * create your own groups with access rights and record rules + * add your own access rights and record rules to an existing group + +Once you have customized your instance properly, you can go to Settings > Modules > Prototype +and create a new prototype: + + * fill in the information of your module (enter the name, the description, the logo, etc.) + * in the Depencencies tab, select all the other modules that yours will be depending on + * in the Data & Demo tab, select your filters for data and demo data + * in the Fields tab, select the fields you created or customized + * in the Interface tab, select your menu items and your views + * in the Security tab, select your groups, access rights and record rules + * save and click on export + +You will get a zip file containing your module ready to be installed and compliant with the +conventions of the OCA. You can then provide the module to a developer who have to implement +things like default values or onchange methods. + +Known issues / Roadmap +====================== + + * `#104`_ - Include controllers.py and templates.xml from scaffold. + * Attach images to the prototype and export them to be used in the 'images' module manifest. + * Add a Report tab to select and export reports + * Add a Workflow tab to select and export workflows, nodes, transitions, actions + +.. _#104: https://github.com/OCA/server-tools/issues/104 + +Please report any idea or issue to https://github.com/OCA/server-tools/issues. + +Credits +======= + +Contributors +------------ + +* David Arnold +* Jordi Riera +* Maxime Chambreuil +* Savoir-faire Linux + +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. + +Versions +======== + +v0.1 +---- + +All the features are not implemented but the minimal is done. +With this version you can: + + The set up of openerp.py is covered, description, maintainer, website, name, technical name... + Views and menus can be set through odoo and gathered in prototype. The files will be automatically generated and add to the data section of the openerp.py. Be aware some advanced feature as domain or context might still missing. + Dependencies can be set throught the Dependency page + Custom fields can be added. A file by model will be generated with all the fields of the model. The init.py files are updated accordingly. Be aware that some features are not implemented yet, as the domain, the context. + +This version should be enought for: + + set up the client module + create a new field in a view. + +Features that you might expect in next version: + + better generation of fields (many2many fields, domain, context) + Generation of data and demo data files. + Security files (rules, ir.model.access.csv) + openerp's description editing README.rst and index.html + module screenshots + +v0.2 +---- + + Renamed from prototype to module_prototyper as discussed in #108 + menu in Settings that gather element used to create a prototype (menu views, views, fields) + +v0.3 +---- + Replace ir.ui.model by ir.ui.view in generated xml views + Improve pep8 compatibility of generation of models + + +Known bugs +---------- + + icon file extension might be wrong. The extension is hard coded for now. It is planned add Document as dependency to handle it. + diff --git a/module_prototyper/__init__.py b/module_prototyper/__init__.py new file mode 100644 index 000000000..f01f7f122 --- /dev/null +++ b/module_prototyper/__init__.py @@ -0,0 +1,25 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2010 - 2014 Savoir-faire Linux +# (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +from . import ( + models, + wizard +) diff --git a/module_prototyper/__openerp__.py b/module_prototyper/__openerp__.py new file mode 100644 index 000000000..8de91bdf4 --- /dev/null +++ b/module_prototyper/__openerp__.py @@ -0,0 +1,45 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2010 - 2014 Savoir-faire Linux +# (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +{ + 'name': 'Module Prototyper', + 'version': '0.3', + 'author': 'Savoir-faire Linux', + 'maintainer': 'Savoir-faire Linux', + 'website': 'http://www.savoirfairelinux.com', + 'license': 'AGPL-3', + 'category': 'Others', + 'summary': 'Prototype your module', + 'depends': [ + 'admin_technical_features', + ], + 'external_dependencies': { + 'python': [], + }, + 'data': [ + 'wizard/module_prototyper_module_export_view.xml', + 'views/module_prototyper_view.xml', + 'views/ir_model_fields_view.xml', + 'security/ir.model.access.csv', + ], + 'installable': True, +} diff --git a/module_prototyper/i18n/fr.po b/module_prototyper/i18n/fr.po new file mode 100644 index 000000000..15c519357 --- /dev/null +++ b/module_prototyper/i18n/fr.po @@ -0,0 +1,338 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * module_prototyper +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 8.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2015-01-02 17:07+0000\n" +"PO-Revision-Date: 2015-01-02 12:18-0500\n" +"Last-Translator: Jordi Riera \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: \n" +"X-Generator: Poedit 1.5.4\n" + +#. module: module_prototyper +#: selection:module_prototyper.module.export,api_version:0 +msgid "8.0" +msgstr "" + +#. module: module_prototyper +#: field:module_prototyper.module.export,api_version:0 +msgid "API version" +msgstr "Version de l'API" + +#. module: module_prototyper +#: field:module_prototyper,right_ids:0 +msgid "Access Rights" +msgstr "Liste des contrôles d'accès" + +#. module: module_prototyper +#: field:module_prototyper,application:0 +msgid "Application" +msgstr "" + +#. module: module_prototyper +#: field:module_prototyper,author:0 +msgid "Author" +msgstr "Auteur" + +#. module: module_prototyper +#: field:module_prototyper,auto_install:0 +msgid "Auto Install" +msgstr "Installation automatique" + +#. module: module_prototyper +#: view:module_prototyper.module.export:module_prototyper.view_module_export_wizard +msgid "Cancel" +msgstr "Annuler" + +#. module: module_prototyper +#: field:module_prototyper,category_id:0 +msgid "Category" +msgstr "Catégorie" + +#. module: module_prototyper +#: help:module_prototyper,application:0 +msgid "Check if the module is an Odoo application." +msgstr "Cocher si le module est une application Odoo." + +#. module: module_prototyper +#: help:module_prototyper,auto_install:0 +msgid "Check if the module should be install by default." +msgstr "Cocher si le module devrait être installé par défaut." + +#. module: module_prototyper +#: view:module_prototyper.module.export:module_prototyper.view_module_export_wizard +msgid "Close" +msgstr "Fermer" + +#. module: module_prototyper +#: field:module_prototyper,create_uid:0 +#: field:module_prototyper.module.export,create_uid:0 +msgid "Created by" +msgstr "Créé par" + +#. module: module_prototyper +#: field:module_prototyper,create_date:0 +#: field:module_prototyper.module.export,create_date:0 +msgid "Created on" +msgstr "Créé le" + +#. module: module_prototyper +#: field:module_prototyper,data_ids:0 +msgid "Data filters" +msgstr "Filtres pour les données" + +#. module: module_prototyper +#: field:module_prototyper,demo_ids:0 +msgid "Demo filters" +msgstr "Filtres pour les données de démo" + +#. module: module_prototyper +#: view:module_prototyper:module_prototyper.module_prototyper_base_form_view +#: field:module_prototyper,dependency_ids:0 +msgid "Dependencies" +msgstr "Dépendances" + +#. module: module_prototyper +#: view:module_prototyper:module_prototyper.module_prototyper_base_form_view +#: field:module_prototyper,description:0 +msgid "Description" +msgstr "Descriptions" + +#. module: module_prototyper +#: view:module_prototyper:module_prototyper.module_prototyper_base_form_view +#: view:module_prototyper.module.export:module_prototyper.view_module_export_wizard +msgid "Export" +msgstr "Exporter" + +#. module: module_prototyper +#: view:module_prototyper.module.export:module_prototyper.view_module_export_wizard +msgid "Export Complete" +msgstr "Export terminé" + +#. module: module_prototyper +#: model:ir.actions.act_window,name:module_prototyper.button_module_export_action +#: view:module_prototyper.module.export:module_prototyper.view_module_export_wizard +msgid "Export Module" +msgstr "Exporter le module" + +#. module: module_prototyper +#: view:module_prototyper.module.export:module_prototyper.view_module_export_wizard +msgid "Export Settings" +msgstr "Configuration de l'export" + +#. module: module_prototyper +#: model:ir.model,name:module_prototyper.model_ir_model_fields +#: model:ir.ui.menu,name:module_prototyper.ir_model_model_fields +#: view:module_prototyper:module_prototyper.module_prototyper_base_form_view +#: field:module_prototyper,field_ids:0 +msgid "Fields" +msgstr "Champs" + +#. module: module_prototyper +#: field:module_prototyper.module.export,data:0 +msgid "File" +msgstr "Fichier" + +#. module: module_prototyper +#: field:module_prototyper.module.export,name:0 +msgid "File Name" +msgstr "Nom du fichier" + +#. module: module_prototyper +#: field:module_prototyper,group_ids:0 +msgid "Groups" +msgstr "Groupes" + +#. module: module_prototyper +#: field:ir.model.fields,helper:0 +msgid "Helper" +msgstr "Texte d'aider" + +#. module: module_prototyper +#: view:module_prototyper.module.export:module_prototyper.view_module_export_wizard +msgid "Here is the exported module:" +msgstr "Voici le module exporté :" + +#. module: module_prototyper +#: field:module_prototyper,id:0 field:module_prototyper.module.export,id:0 +msgid "ID" +msgstr "" + +#. module: module_prototyper +#: field:module_prototyper,icon_image:0 +msgid "Icon" +msgstr "Icône" + +#. module: module_prototyper +#: view:module_prototyper:module_prototyper.module_prototyper_base_form_view +msgid "Interface" +msgstr "" + +#. module: module_prototyper +#: field:module_prototyper,write_uid:0 +#: field:module_prototyper.module.export,write_uid:0 +msgid "Last Updated by" +msgstr "Mis à jour par" + +#. module: module_prototyper +#: field:module_prototyper,write_date:0 +#: field:module_prototyper.module.export,write_date:0 +msgid "Last Updated on" +msgstr "Mis à jour le" + +#. module: module_prototyper +#: field:module_prototyper,licence:0 +msgid "Licence" +msgstr "" + +#. module: module_prototyper +#: field:module_prototyper,maintainer:0 +msgid "Maintainer" +msgstr "Mainteneur" + +#. module: module_prototyper +#: model:ir.ui.menu,name:module_prototyper.menu_grant_menu_access +#: field:module_prototyper,menu_ids:0 +msgid "Menu Items" +msgstr "Éléments du menu" + +#. module: module_prototyper +#: view:module_prototyper:module_prototyper.module_prototyper_base_form_view +msgid "Module" +msgstr "" + +#. module: module_prototyper +#: field:module_prototyper,human_name:0 +msgid "Module Name" +msgstr "Nom du module" + +#. module: module_prototyper +#: model:ir.model,name:module_prototyper.model_module_prototyper +msgid "Module Prototyper" +msgstr "" + +#. module: module_prototyper +#: model:ir.ui.menu,name:module_prototyper.menu_module_prototyper +msgid "Module Prototypers" +msgstr "" + +#. module: module_prototyper +#: view:ir.model:module_prototyper.view_ir_model_form +msgid "Notes" +msgstr "" + +#. module: module_prototyper +#: field:ir.model.fields,notes:0 +msgid "Notes to developers." +msgstr "Notes aux développeurs." + +#. module: module_prototyper +#: view:ir.model.fields:module_prototyper.view_ir_model_fields_form +msgid "" +"Notes to help developers to understand the work or advanced features that " +"should be added, ie: onchange, etc." +msgstr "" +"Notes pour aider les développeurs à comprendre le travail effectué ou les " +"ajouts à faire. Exemple: onchanges, etc." + +#. module: module_prototyper +#: view:module_prototyper:module_prototyper.module_prototyper_base_tree_view +msgid "Prototype" +msgstr "" + +#. module: module_prototyper +#: model:ir.actions.act_window,name:module_prototyper.open_module_prototyper_list +#: model:ir.ui.menu,name:module_prototyper.menu_open_module_prototyper +msgid "Prototypes" +msgstr "" + +#. module: module_prototyper +#: field:module_prototyper,rule_ids:0 +msgid "Record Rules" +msgstr "Règles sur les enregistrements" + +#. module: module_prototyper +#: field:module_prototyper.module.export,state:0 +msgid "State" +msgstr "État" + +#. module: module_prototyper +#: field:module_prototyper,summary:0 +msgid "Summary" +msgstr "Résumé" + +#. module: module_prototyper +#: field:module_prototyper,name:0 +msgid "Technical Name" +msgstr "Nom technique" + +#. module: module_prototyper +#: view:ir.model.fields:module_prototyper.view_ir_model_fields_form +msgid "Text that will be set as the helper of the field..." +msgstr "Texte qui sera placé en tant que texte d'aide du champs." + +#. module: module_prototyper +#: help:module_prototyper,human_name:0 +msgid "" +"The Module Name will be used as the displayed name of the exported module." +msgstr "Le nom du module sera affiché en tant que nom du module exporté." + +#. module: module_prototyper +#: help:module_prototyper,icon_image:0 +msgid "" +"The icon set up here will be used as the icon for the exported module also" +msgstr "L'icône sera utilisé comme icône du module exporté." + +#. module: module_prototyper +#: help:module_prototyper,data_ids:0 +msgid "The records matching the filters will be added as data." +msgstr "" +"Les enregistrements correspondant aux critères des filtres seront exportées " +"dans le module comme données." + +#. module: module_prototyper +#: help:module_prototyper,demo_ids:0 +msgid "The records matching the filters will be added as demo data." +msgstr "" +"Les enregistrements correspondant aux critères des filtres seront exportées " +"dans le module comme données de démo." + +#. module: module_prototyper +#: help:module_prototyper,name:0 +msgid "" +"The technical name will be used to define the name of the exported module, " +"the name of the model." +msgstr "Le nom technique sera utilisé comme nom du module exporté." + +#. module: module_prototyper +#: field:module_prototyper,version:0 +msgid "Version" +msgstr "" + +#. module: module_prototyper +#: model:ir.ui.menu,name:module_prototyper.menu_action_ui_view +#: field:module_prototyper,view_ids:0 +msgid "Views" +msgstr "Vues" + +#. module: module_prototyper +#: field:module_prototyper,website:0 +msgid "Website" +msgstr "Site web" + +#. module: module_prototyper +#: selection:module_prototyper.module.export,state:0 +msgid "choose" +msgstr "choisir" + +#. module: module_prototyper +#: selection:module_prototyper.module.export,state:0 +msgid "get" +msgstr "récupérer" diff --git a/module_prototyper/i18n/module_prototyper.pot b/module_prototyper/i18n/module_prototyper.pot new file mode 100644 index 000000000..6584e9e03 --- /dev/null +++ b/module_prototyper/i18n/module_prototyper.pot @@ -0,0 +1,327 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * module_prototyper +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 8.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2015-01-02 17:07+0000\n" +"PO-Revision-Date: 2015-01-02 17:07+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: module_prototyper +#: selection:module_prototyper.module.export,api_version:0 +msgid "8.0" +msgstr "" + +#. module: module_prototyper +#: field:module_prototyper.module.export,api_version:0 +msgid "API version" +msgstr "" + +#. module: module_prototyper +#: field:module_prototyper,right_ids:0 +msgid "Access Rights" +msgstr "" + +#. module: module_prototyper +#: field:module_prototyper,application:0 +msgid "Application" +msgstr "" + +#. module: module_prototyper +#: field:module_prototyper,author:0 +msgid "Author" +msgstr "" + +#. module: module_prototyper +#: field:module_prototyper,auto_install:0 +msgid "Auto Install" +msgstr "" + +#. module: module_prototyper +#: view:module_prototyper.module.export:module_prototyper.view_module_export_wizard +msgid "Cancel" +msgstr "" + +#. module: module_prototyper +#: field:module_prototyper,category_id:0 +msgid "Category" +msgstr "" + +#. module: module_prototyper +#: help:module_prototyper,application:0 +msgid "Check if the module is an Odoo application." +msgstr "" + +#. module: module_prototyper +#: help:module_prototyper,auto_install:0 +msgid "Check if the module should be install by default." +msgstr "" + +#. module: module_prototyper +#: view:module_prototyper.module.export:module_prototyper.view_module_export_wizard +msgid "Close" +msgstr "" + +#. module: module_prototyper +#: field:module_prototyper,create_uid:0 +#: field:module_prototyper.module.export,create_uid:0 +msgid "Created by" +msgstr "" + +#. module: module_prototyper +#: field:module_prototyper,create_date:0 +#: field:module_prototyper.module.export,create_date:0 +msgid "Created on" +msgstr "" + +#. module: module_prototyper +#: field:module_prototyper,data_ids:0 +msgid "Data filters" +msgstr "" + +#. module: module_prototyper +#: field:module_prototyper,demo_ids:0 +msgid "Demo filters" +msgstr "" + +#. module: module_prototyper +#: view:module_prototyper:module_prototyper.module_prototyper_base_form_view +#: field:module_prototyper,dependency_ids:0 +msgid "Dependencies" +msgstr "" + +#. module: module_prototyper +#: view:module_prototyper:module_prototyper.module_prototyper_base_form_view +#: field:module_prototyper,description:0 +msgid "Description" +msgstr "" + +#. module: module_prototyper +#: view:module_prototyper:module_prototyper.module_prototyper_base_form_view +#: view:module_prototyper.module.export:module_prototyper.view_module_export_wizard +msgid "Export" +msgstr "" + +#. module: module_prototyper +#: view:module_prototyper.module.export:module_prototyper.view_module_export_wizard +msgid "Export Complete" +msgstr "" + +#. module: module_prototyper +#: model:ir.actions.act_window,name:module_prototyper.button_module_export_action +#: view:module_prototyper.module.export:module_prototyper.view_module_export_wizard +msgid "Export Module" +msgstr "" + +#. module: module_prototyper +#: view:module_prototyper.module.export:module_prototyper.view_module_export_wizard +msgid "Export Settings" +msgstr "" + +#. module: module_prototyper +#: model:ir.model,name:module_prototyper.model_ir_model_fields +#: model:ir.ui.menu,name:module_prototyper.ir_model_model_fields +#: view:module_prototyper:module_prototyper.module_prototyper_base_form_view +#: field:module_prototyper,field_ids:0 +msgid "Fields" +msgstr "" + +#. module: module_prototyper +#: field:module_prototyper.module.export,data:0 +msgid "File" +msgstr "" + +#. module: module_prototyper +#: field:module_prototyper.module.export,name:0 +msgid "File Name" +msgstr "" + +#. module: module_prototyper +#: field:module_prototyper,group_ids:0 +msgid "Groups" +msgstr "" + +#. module: module_prototyper +#: field:ir.model.fields,helper:0 +msgid "Helper" +msgstr "" + +#. module: module_prototyper +#: view:module_prototyper.module.export:module_prototyper.view_module_export_wizard +msgid "Here is the exported module:" +msgstr "" + +#. module: module_prototyper +#: field:module_prototyper,id:0 +#: field:module_prototyper.module.export,id:0 +msgid "ID" +msgstr "" + +#. module: module_prototyper +#: field:module_prototyper,icon_image:0 +msgid "Icon" +msgstr "" + +#. module: module_prototyper +#: view:module_prototyper:module_prototyper.module_prototyper_base_form_view +msgid "Interface" +msgstr "" + +#. module: module_prototyper +#: field:module_prototyper,write_uid:0 +#: field:module_prototyper.module.export,write_uid:0 +msgid "Last Updated by" +msgstr "" + +#. module: module_prototyper +#: field:module_prototyper,write_date:0 +#: field:module_prototyper.module.export,write_date:0 +msgid "Last Updated on" +msgstr "" + +#. module: module_prototyper +#: field:module_prototyper,licence:0 +msgid "Licence" +msgstr "" + +#. module: module_prototyper +#: field:module_prototyper,maintainer:0 +msgid "Maintainer" +msgstr "" + +#. module: module_prototyper +#: model:ir.ui.menu,name:module_prototyper.menu_grant_menu_access +#: field:module_prototyper,menu_ids:0 +msgid "Menu Items" +msgstr "" + +#. module: module_prototyper +#: view:module_prototyper:module_prototyper.module_prototyper_base_form_view +msgid "Module" +msgstr "" + +#. module: module_prototyper +#: field:module_prototyper,human_name:0 +msgid "Module Name" +msgstr "" + +#. module: module_prototyper +#: model:ir.model,name:module_prototyper.model_module_prototyper +msgid "Module Prototyper" +msgstr "" + +#. module: module_prototyper +#: model:ir.ui.menu,name:module_prototyper.menu_module_prototyper +msgid "Module Prototypers" +msgstr "" + +#. module: module_prototyper +#: view:ir.model:module_prototyper.view_ir_model_form +msgid "Notes" +msgstr "" + +#. module: module_prototyper +#: field:ir.model.fields,notes:0 +msgid "Notes to developers." +msgstr "" + +#. module: module_prototyper +#: view:ir.model.fields:module_prototyper.view_ir_model_fields_form +msgid "Notes to help developers to understand the work or advanced features that should be added, ie: onchange, etc." +msgstr "" + +#. module: module_prototyper +#: view:module_prototyper:module_prototyper.module_prototyper_base_tree_view +msgid "Prototype" +msgstr "" + +#. module: module_prototyper +#: model:ir.actions.act_window,name:module_prototyper.open_module_prototyper_list +#: model:ir.ui.menu,name:module_prototyper.menu_open_module_prototyper +msgid "Prototypes" +msgstr "" + +#. module: module_prototyper +#: field:module_prototyper,rule_ids:0 +msgid "Record Rules" +msgstr "" + +#. module: module_prototyper +#: field:module_prototyper.module.export,state:0 +msgid "State" +msgstr "" + +#. module: module_prototyper +#: field:module_prototyper,summary:0 +msgid "Summary" +msgstr "" + +#. module: module_prototyper +#: field:module_prototyper,name:0 +msgid "Technical Name" +msgstr "" + +#. module: module_prototyper +#: view:ir.model.fields:module_prototyper.view_ir_model_fields_form +msgid "Text that will be set as the helper of the field..." +msgstr "" + +#. module: module_prototyper +#: help:module_prototyper,human_name:0 +msgid "The Module Name will be used as the displayed name of the exported module." +msgstr "" + +#. module: module_prototyper +#: help:module_prototyper,icon_image:0 +msgid "The icon set up here will be used as the icon for the exported module also" +msgstr "" + +#. module: module_prototyper +#: help:module_prototyper,data_ids:0 +msgid "The records matching the filters will be added as data." +msgstr "" + +#. module: module_prototyper +#: help:module_prototyper,demo_ids:0 +msgid "The records matching the filters will be added as demo data." +msgstr "" + +#. module: module_prototyper +#: help:module_prototyper,name:0 +msgid "The technical name will be used to define the name of the exported module, the name of the model." +msgstr "" + +#. module: module_prototyper +#: field:module_prototyper,version:0 +msgid "Version" +msgstr "" + +#. module: module_prototyper +#: model:ir.ui.menu,name:module_prototyper.menu_action_ui_view +#: field:module_prototyper,view_ids:0 +msgid "Views" +msgstr "" + +#. module: module_prototyper +#: field:module_prototyper,website:0 +msgid "Website" +msgstr "" + +#. module: module_prototyper +#: selection:module_prototyper.module.export,state:0 +msgid "choose" +msgstr "" + +#. module: module_prototyper +#: selection:module_prototyper.module.export,state:0 +msgid "get" +msgstr "" + diff --git a/module_prototyper/models/__init__.py b/module_prototyper/models/__init__.py new file mode 100644 index 000000000..39ab18bec --- /dev/null +++ b/module_prototyper/models/__init__.py @@ -0,0 +1,25 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2010 - 2014 Savoir-faire Linux +# (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +from . import ( + module_prototyper, + ir_model_fields, +) diff --git a/module_prototyper/models/ir_model_fields.py b/module_prototyper/models/ir_model_fields.py new file mode 100644 index 000000000..80b9842af --- /dev/null +++ b/module_prototyper/models/ir_model_fields.py @@ -0,0 +1,30 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2010 - 2014 Savoir-faire Linux +# (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +from openerp import fields, models + + +class ir_model_fields(models.Model): + """Addition of text fields to fields.""" + _inherit = "ir.model.fields" + + notes = fields.Text('Notes to developers.') + helper = fields.Text('Helper') diff --git a/module_prototyper/models/module_prototyper.py b/module_prototyper/models/module_prototyper.py new file mode 100644 index 000000000..61b9281d1 --- /dev/null +++ b/module_prototyper/models/module_prototyper.py @@ -0,0 +1,358 @@ +# -*- encoding: utf-8 -*- +# ############################################################################# +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2010 - 2014 Savoir-faire Linux +# (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +import os +import re +import base64 +from datetime import date +YEAR = date.today().year + +from collections import namedtuple +from jinja2 import Environment, FileSystemLoader +from openerp import models, api, fields + + +class ModulePrototyper(models.Model): + """Module Prototyper gathers different information from all over the + database to build a prototype of module. + We are calling it a prototype as it will most likely need to be reviewed + by a developer to fix glitch that would sneak it during the generation of + files but also to add not supported features. + """ + _name = "module_prototyper" + _description = "Module Prototyper" + + # In prevision of odoo might support other licences than AGPL in + # (near) future. + licence = fields.Char( + 'Licence', + default='AGPL-3', + ) + name = fields.Char( + 'Technical Name', required=True, + help=('The technical name will be used to define the name of ' + 'the exported module, the name of the model.') + ) + category_id = fields.Many2one('ir.module.category', 'Category') + human_name = fields.Char( + 'Module Name', required=True, + help=('The Module Name will be used as the displayed name of the ' + 'exported module.') + ) + summary = fields.Char('Summary', required=True) + description = fields.Text('Description', required=True) + author = fields.Char('Author', required=True) + maintainer = fields.Char('Maintainer') + website = fields.Char('Website') + icon_image = fields.Binary( + 'Icon', + help=('The icon set up here will be used as the icon ' + 'for the exported module also') + ) + version = fields.Char('Version', size=3, default='0.1') + auto_install = fields.Boolean( + 'Auto Install', + default=False, + help='Check if the module should be install by default.' + ) + application = fields.Boolean( + 'Application', + default=False, + help='Check if the module is an Odoo application.' + ) + # Relations + dependency_ids = fields.Many2many( + 'ir.module.module', 'module_prototyper_module_rel', + 'module_prototyper_id', 'module_id', + 'Dependencies' + ) + data_ids = fields.Many2many( + 'ir.filters', + 'prototype_data_rel', + 'module_prototyper_id', 'filter_id', + 'Data filters', + help="The records matching the filters will be added as data." + ) + demo_ids = fields.Many2many( + 'ir.filters', + 'prototype_demo_rel', + 'module_prototyper_id', 'filter_id', + 'Demo filters', + help="The records matching the filters will be added as demo data." + ) + field_ids = fields.Many2many( + 'ir.model.fields', 'prototype_fields_rel', + 'module_prototyper_id', 'field_id', 'Fields' + ) + menu_ids = fields.Many2many( + 'ir.ui.menu', 'prototype_menu_rel', + 'module_prototyper_id', 'menu_id', 'Menu Items' + ) + view_ids = fields.Many2many( + 'ir.ui.view', 'prototype_view_rel', + 'module_prototyper_id', 'view_id', 'Views' + ) + group_ids = fields.Many2many( + 'res.groups', 'prototype_groups_rel', + 'module_prototyper_id', 'group_id', 'Groups' + ) + right_ids = fields.Many2many( + 'ir.model.access', 'prototype_rights_rel', + 'module_prototyper_id', 'right_id', + 'Access Rights' + ) + rule_ids = fields.Many2many( + 'ir.rule', 'prototype_rule_rel', + 'module_prototyper_id', 'rule_id', 'Record Rules' + ) + + __data_files = [] + __field_descriptions = {} + _env = None + File_details = namedtuple('file_details', ['filename', 'filecontent']) + template_path = '{}/../templates/'.format(os.path.dirname(__file__)) + + @api.model + def set_jinja_env(self, api_version): + """Set the Jinja2 environment. + The environment will helps the system to find the templates to render. + :param api_version: string, odoo api + :return: jinja2.Environment instance. + """ + if self._env is None: + self._env = Environment( + loader=FileSystemLoader( + os.path.join(self.template_path, api_version) + ) + ) + return self._env + + def set_field_descriptions(self): + """Mock the list of fields into dictionary. + It allows us to add or change attributes of the fields. + + :return: None + """ + for field in self.field_ids: + field_description = {} + # This will mock a field record. + # the mock will allow us to add data or modify the data + # of the field (like for the name) with keeping all the + # attributes of the record. + field_description.update({ + attr_name: getattr(field, attr_name) + for attr_name in dir(field) + if not attr_name[0] == '_' + }) + # custom fields start with the prefix x_. + # it has to be removed. + field_description['name'] = re.sub(r'^x_', '', field.name) + self.__field_descriptions[field] = field_description + + @api.model + def generate_files(self): + """ Generates the files from the details of the prototype. + :return: tuple + """ + assert self._env is not None, \ + 'Run set_env(api_version) before to generate files.' + + self.set_field_descriptions() + file_details = [] + file_details.extend(self.generate_models_details()) + file_details.extend(self.generate_views_details()) + file_details.extend(self.generate_menus_details()) + file_details.append(self.generate_module_init_file_details()) + # must be the last as the other generations might add information + # to put in the __openerp__: additional dependencies, views files, etc. + file_details.append(self.generate_module_openerp_file_details()) + if self.icon_image: + file_details.append(self.save_icon()) + + return file_details + + @api.model + def save_icon(self): + """Save the icon of the prototype as a image. + The image is used afterwards as the icon of the exported module. + + :return: FileDetails instance + """ + # TODO: The image is not always a jpg. + # 2 ways to do it: + # * find a way to detect image type from the data + # * add document as a dependency. + # The second options seems to be better, as Document is a base module. + return self.File_details( + os.path.join('static', 'description', 'icon.jpg'), + base64.b64decode(self.icon_image) + ) + + @api.model + def generate_module_openerp_file_details(self): + """Wrapper to generate the __openerp__.py file of the module.""" + return self.generate_file_details( + '__openerp__.py', + '__openerp__.py.template', + prototype=self, + data_files=self.__data_files, + ) + + @api.model + def generate_module_init_file_details(self): + """Wrapper to generate the __init__.py file of the module.""" + return self.generate_file_details( + '__init__.py', + '__init__.py.template', + # no import models if no work of fields in + # the prototype + models=bool(self.field_ids) + ) + + @api.model + def generate_models_details(self): + """Finds the models from the list of fields and generates + the __init__ file and each models files (one by class). + """ + files = [] + # TODO: doesn't work as need to find the module to import + # and it is not necessary the name of the model the fields + # belongs to. + # ie. field.cell_phone is defined in a model inheriting from + # res.partner. + # How do we find the module the field was defined in? + # dependencies = set([dep.id for dep in self.dependencies]) + + relations = {} + for field in self.__field_descriptions.itervalues(): + model = field.get('model_id') + relations.setdefault(model, []).append(field) + # dependencies.add(model.id) + + # blind update of dependencies. + # self.write({ + # 'dependencies': [(6, 0, [id_ for id_ in dependencies])] + # }) + + files.append(self.generate_models_init_details(relations.keys())) + for model, custom_fields in relations.iteritems(): + files.append(self.generate_model_details(model, custom_fields)) + + return files + + @api.model + def generate_models_init_details(self, ir_models): + """Wrapper to generate the __init__.py file in models folder.""" + return self.generate_file_details( + 'models/__init__.py', + 'models/__init__.py.template', + models=[ + self.friendly_name(ir_model.model) + for ir_model in ir_models + ] + ) + + @api.model + def generate_views_details(self): + """Wrapper to generate the views files.""" + relations = {} + for view in self.view_ids: + relations.setdefault(view.model, []).append(view) + + views_details = [] + for model, views in relations.iteritems(): + filepath = 'views/{}_view.xml'.format( + self.friendly_name(model) + ) + views_details.append( + self.generate_file_details( + filepath, + 'views/model_views.xml.template', + views=views + ) + ) + self.__data_files.append(filepath) + + return views_details + + @api.model + def generate_menus_details(self): + """Wrapper to generate the menus files.""" + relations = {} + for menu in self.menu_ids: + relations.setdefault(menu.action.res_model, []).append(menu) + + menus_details = [] + for model_name, menus in relations.iteritems(): + filepath = 'views/{}_menus.xml'.format( + self.friendly_name(model_name) + ) + menus_details.append( + self.generate_file_details( + filepath, + 'views/model_menus.xml.template', + menus=menus, + ) + ) + self.__data_files.append(filepath) + + return menus_details + + @api.model + def generate_model_details(self, model, field_descriptions): + """Wrapper to generate the python file for the model. + + :param model: ir.model record. + :param field_descriptions: list of ir.model.fields records. + :return: FileDetails instance. + """ + python_friendly_name = self.friendly_name(model.model) + return self.generate_file_details( + 'models/{}.py'.format(python_friendly_name), + 'models/model_name.py.template', + name=python_friendly_name, + inherit=model.model, + fields=field_descriptions, + ) + + @staticmethod + def friendly_name(name): + return name.replace('.', '_') + + @api.model + def generate_file_details(self, filename, template, **kwargs): + """ generate file details from jinja2 template. + :param filename: name of the file the content is related to + :param template: path to the file to render the content + :param kwargs: arguments of the template + :return: File_details instance + """ + template = self._env.get_template(template) + # keywords used in several templates. + kwargs.update( + { + 'export_year': YEAR, + 'author': self.author, + 'website': self.website, + 'cr': self._cr, + } + ) + return self.File_details(filename, template.render(kwargs)) diff --git a/module_prototyper/security/ir.model.access.csv b/module_prototyper/security/ir.model.access.csv new file mode 100755 index 000000000..7250a7457 --- /dev/null +++ b/module_prototyper/security/ir.model.access.csv @@ -0,0 +1,2 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_prototype_admin,prototype_system,model_module_prototyper,base.group_system,1,1,1,1 diff --git a/module_prototyper/static/description/icon.png b/module_prototyper/static/description/icon.png new file mode 100644 index 000000000..6a120f9fd Binary files /dev/null and b/module_prototyper/static/description/icon.png differ diff --git a/module_prototyper/templates/8.0/__init__.py.template b/module_prototyper/templates/8.0/__init__.py.template new file mode 100644 index 000000000..82452828d --- /dev/null +++ b/module_prototyper/templates/8.0/__init__.py.template @@ -0,0 +1,6 @@ +{% extends "header.template" %} +{% block body %} +{% if models -%} +from . import models +{% endif %} +{% endblock %} diff --git a/module_prototyper/templates/8.0/__openerp__.py.template b/module_prototyper/templates/8.0/__openerp__.py.template new file mode 100644 index 000000000..23d1125cb --- /dev/null +++ b/module_prototyper/templates/8.0/__openerp__.py.template @@ -0,0 +1,56 @@ +{% extends "header.template" %} +{% block body %} +{ + 'name': '{{ prototype.name }}', + 'version': '{{ prototype.version }}', + 'author': '{{ prototype.author }}', + 'maintainer': '{{ prototype.maintainer }}', + 'website': '{{ prototype.website }}', + 'license': '{{ prototype.licence }}', + + # Categories can be used to filter modules in modules listing + # Check https://github.com/odoo/odoo/blob/master/openerp/addons/base/module/module_data.xml + # for the full list + 'category': '{{ prototype.category_id.name }}', + 'summary': '{{ prototype.summary }}', + 'description': """ +{{ prototype.description }} + +* Module exported by the Module Prototyper module for version 8.0. +* If you have any questions, please contact Savoir-faire Linux +(support@savoirfairelinux.com) +""", + + # any module necessary for this one to work correctly + 'depends': [ + {% for dependency in prototype.dependency_ids -%} + '{{ dependency.name }}', + {% endfor -%}], + 'external_dependencies': { + 'python': [], + }, + + # always loaded + 'data': [ + {% for data_file in data_files -%} + '{{ data_file }}', + {% endfor -%}], + # only loaded in demonstration mode + 'demo': [ + {% for demo_file in prototype.demo_ids -%} + '{{ demo_file.name }}', + {% endfor -%}], + + # used for Javascript Web CLient Testing with QUnit / PhantomJS + # https://www.odoo.com/documentation/8.0/reference/javascript.html#testing-in-odoo-web-client + 'js': [], + 'css': [], + 'qweb': [], + + 'installable': True, + # Install this module automatically if all dependency have been previously and independently installed. + # Used for synergetic or glue modules. + 'auto_install': {{ prototype.auto_install }}, + 'application': {{ prototype.application }}, +} +{% endblock %} diff --git a/module_prototyper/templates/8.0/data/model_name.xml.template b/module_prototyper/templates/8.0/data/model_name.xml.template new file mode 100644 index 000000000..7b25f11f8 --- /dev/null +++ b/module_prototyper/templates/8.0/data/model_name.xml.template @@ -0,0 +1,8 @@ + + + + + {{ data }} + + + diff --git a/module_prototyper/templates/8.0/demo/model_name.xml.template b/module_prototyper/templates/8.0/demo/model_name.xml.template new file mode 100644 index 000000000..097c294f1 --- /dev/null +++ b/module_prototyper/templates/8.0/demo/model_name.xml.template @@ -0,0 +1,8 @@ + + + + + {{ demo }} + + + diff --git a/module_prototyper/templates/8.0/header.template b/module_prototyper/templates/8.0/header.template new file mode 100644 index 000000000..2e6251e7b --- /dev/null +++ b/module_prototyper/templates/8.0/header.template @@ -0,0 +1,22 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# Odoo, Open Source Management Solution +# This module copyright (C) {{ export_year }} {% if author %}{{ author }}{% endif %} +# {% if website %}({{ website }}).{% endif %} +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +{% block body %}{% endblock %} \ No newline at end of file diff --git a/module_prototyper/templates/8.0/models/__init__.py.template b/module_prototyper/templates/8.0/models/__init__.py.template new file mode 100644 index 000000000..caf9d7c74 --- /dev/null +++ b/module_prototyper/templates/8.0/models/__init__.py.template @@ -0,0 +1,6 @@ +{% extends "header.template" %} +{% block body %} +{% for model in models -%} +from . import {{ model }} +{% endfor %} +{% endblock %} diff --git a/module_prototyper/templates/8.0/models/model_name.py.template b/module_prototyper/templates/8.0/models/model_name.py.template new file mode 100644 index 000000000..8f8ff5378 --- /dev/null +++ b/module_prototyper/templates/8.0/models/model_name.py.template @@ -0,0 +1,22 @@ +{% extends "header.template" %} +{% block body %} +from openerp import models, fields +from openerp.tools.translate import _ + + +class {{ name }}(models.Model): + _inherit = "{{ inherit }}" + {% if description -%}_description = "{{ description }}"{% endif %} + + {% for field in fields -%} + {% if field.notes -%}# {{ field.notes }}{% endif %} + {{ field.name }} = fields.{{ field.ttype|capitalize }}( + string=_("{{ field.field_description }}"), + required={{ field.required }}, + translate={{ field.translate }}, + readonly={{ field.readonly }}, + {% if field.size -%}size={{ field.size }},{% endif %} + {% if field.helper -%}help=_("{{ field.helper }}"),{% endif %} + ){% endfor %} + +{% endblock %} diff --git a/module_prototyper/templates/8.0/security/ir.model.access.csv.template b/module_prototyper/templates/8.0/security/ir.model.access.csv.template new file mode 100644 index 000000000..0d2111f9d --- /dev/null +++ b/module_prototyper/templates/8.0/security/ir.model.access.csv.template @@ -0,0 +1,4 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +{% for rule in access_rules %} +{{ rule.id }},{{ rule.name }},{{ rule.model_id.id }},{{ rule.group_id.id }},{{ rule.perm_write }},{{ rule.perm_write }},{{ rule.perm_create }},{{ rule.perm_unlink }} +{% endfor %} diff --git a/module_prototyper/templates/8.0/security/model_name.xml.template b/module_prototyper/templates/8.0/security/model_name.xml.template new file mode 100644 index 000000000..6f2d4e508 --- /dev/null +++ b/module_prototyper/templates/8.0/security/model_name.xml.template @@ -0,0 +1,10 @@ + + + + + {{ groups }} + + {{ rules }} + + + diff --git a/module_prototyper/templates/8.0/views/model_menus.xml.template b/module_prototyper/templates/8.0/views/model_menus.xml.template new file mode 100644 index 000000000..642a682d8 --- /dev/null +++ b/module_prototyper/templates/8.0/views/model_menus.xml.template @@ -0,0 +1,24 @@ + + + + {% for menu in menus -%} + + {{ menu.action.name }} + {{ menu.action.type }} + {{ menu.action.res_model }} + {{ menu.action.view_type }} + {{ menu.action.view_mode }} + {% if menu.action.help %}{{ menu.action.help }} + {% endif %} + + + + {% endfor -%} + + diff --git a/module_prototyper/templates/8.0/views/model_views.xml.template b/module_prototyper/templates/8.0/views/model_views.xml.template new file mode 100644 index 000000000..b919abea7 --- /dev/null +++ b/module_prototyper/templates/8.0/views/model_views.xml.template @@ -0,0 +1,23 @@ + + + + + + {% for view in views -%} + + {{ view.name }}.view + {{ view.model }} + {{ view.type }} + + + {# the arch given by odoo start with an xml tag that + will break the xml tree we build. + Be careful, custom field have a x_ prefix that has to be + removed + #} + {{ view.arch|replace('"', "'")|replace("", "")|replace("'x_", "'")|replace("> ", "/>") }} + + + {% endfor %} + + diff --git a/module_prototyper/tests/__init__.py b/module_prototyper/tests/__init__.py new file mode 100644 index 000000000..cb0e34aff --- /dev/null +++ b/module_prototyper/tests/__init__.py @@ -0,0 +1,28 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2010 - 2014 Savoir-faire Linux +# (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +from . import test_prototype_module_export +from . import test_prototype + +checks = [ + test_prototype_module_export, + test_prototype, +] diff --git a/module_prototyper/tests/test_prototype.py b/module_prototyper/tests/test_prototype.py new file mode 100644 index 000000000..0ffdc0707 --- /dev/null +++ b/module_prototyper/tests/test_prototype.py @@ -0,0 +1,83 @@ +# -*- encoding: utf-8 -*- # +# OpenERP, Open Source Management Solution +# This module copyright (C) 2013 Savoir-faire Linux +# (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +from jinja2 import Environment +from jinja2.exceptions import TemplateNotFound +from openerp.tests import common + + +class TestModulePrototyper(common.TransactionCase): + def setUp(self): + super(TestModulePrototyper, self).setUp() + self.main_model = self.env['module_prototyper'] + self.module_category_model = self.env['ir.module.category'] + self.module_module_model = self.env['ir.module.module'] + + self.prototype = self.main_model.create({ + 'name': 't_name', + 'category_id': self.module_category_model.browse(1).id, + 'human_name': 't_human_name', + 'summary': 't_summary', + 'description': 't_description', + 'author': 't_author', + 'maintainer': 't_maintainer', + 'website': 't_website', + 'dependencies': [(6, 0, [1, 2, 3, 4])], + }) + self.api_version = '8.0' + + def test_generate_files_assert_if_no_env(self): + self.assertRaises( + AssertionError, + self.prototype.generate_files + ) + + def test_generate_files(self): + """Test generate_files returns a tuple.""" + self.prototype.set_jinja_env(self.api_version) + details = self.prototype.generate_files() + self.assertIsInstance(details, list) + # namedtuples in tuple + for file_details in details: + self.assertIsInstance(file_details, tuple) + self.assertIsInstance(file_details.filename, basestring) + self.assertIsInstance(file_details.filecontent, basestring) + + def test_generate_files_raise_templatenotfound_if_not_found(self): + self.prototype.set_jinja_env('t_api_version') + self.assertRaises( + TemplateNotFound, + self.prototype.generate_files + ) + + def test_set_env(self): + """test the jinja2 environment is set.""" + self.assertIsNone(self.prototype._env) + self.prototype.set_jinja_env(self.api_version) + self.assertIsInstance( + self.prototype._env, Environment + ) + + def test_friendly_name_return(self): + """Test if the returns match the pattern.""" + name = 'res.partner' + self.assertEqual( + self.prototype.friendly_name(name), + name.replace('.', '_') + ) diff --git a/module_prototyper/tests/test_prototype_module_export.py b/module_prototyper/tests/test_prototype_module_export.py new file mode 100644 index 000000000..4953689c8 --- /dev/null +++ b/module_prototyper/tests/test_prototype_module_export.py @@ -0,0 +1,83 @@ +# -*- encoding: utf-8 -*- +# ############################################################################# +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2010 - 2014 Savoir-faire Linux +# (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +from openerp.tests import common +import zipfile +import StringIO + + +class test_prototype_module_export(common.TransactionCase): + def setUp(self): + super(test_prototype_module_export, self).setUp() + self.main_model = self.env['module_prototyper.module.export'] + self.prototype_model = self.env['module_prototyper'] + self.module_category_model = self.env[ + 'ir.module.category' + ] + + self.prototype = self.prototype_model.create({ + 'name': 't_name', + 'category_id': self.module_category_model.browse(1).id, + 'human_name': 't_human_name', + 'summary': 't_summary', + 'description': 't_description', + 'author': 't_author', + 'maintainer': 't_maintainer', + 'website': 't_website', + }) + + self.exporter = self.main_model.create({'name': 't_name'}) + + def test_action_export_assert_for_wrong_active_model(self): + """Test if the assertion raises.""" + exporter = self.main_model.with_context( + active_model='t_active_model' + ).create({}) + self.assertRaises( + AssertionError, + exporter.action_export, + [exporter.id], + ) + + def test_action_export_update_wizard(self): + """Test if the wizard is updated during the process.""" + exporter = self.main_model.with_context( + active_model=self.prototype_model._name, + active_id=self.prototype.id + ).create({}) + exporter.action_export(exporter.id) + self.assertEqual(exporter.state, 'get') + self.assertEqual(exporter.name, '{}.zip'.format(self.prototype.name)) + + def test_zip_files_returns_tuple(self): + """Test the method return of the method that generate the zip file.""" + file_details = ( + ('test.txt', 'generated'), + ) + ret = self.main_model.zip_files(file_details) + self.assertIsInstance(ret, tuple) + self.assertIsInstance( + ret.zip_file, zipfile.ZipFile + ) + + self.assertIsInstance( + ret.stringIO, StringIO.StringIO + ) diff --git a/module_prototyper/views/ir_model_fields_view.xml b/module_prototyper/views/ir_model_fields_view.xml new file mode 100644 index 000000000..39e659900 --- /dev/null +++ b/module_prototyper/views/ir_model_fields_view.xml @@ -0,0 +1,35 @@ + + + + + + view_ir_model_fields_form + ir.model.fields + + + + + + + view_ir_model_form + ir.model + + + + + + + + + + + diff --git a/module_prototyper/views/module_prototyper_view.xml b/module_prototyper/views/module_prototyper_view.xml new file mode 100644 index 000000000..eafdbf21c --- /dev/null +++ b/module_prototyper/views/module_prototyper_view.xml @@ -0,0 +1,131 @@ + + + + + + Base tree View for module prototypes + module_prototyper + + + + + + + + + + + Base form view for module prototypes + module_prototyper + +
+ + + +
+

+ +

+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ + + Prototypes + module_prototyper + form + tree,form + + + + + + + + + + + + + + + + +
+
diff --git a/module_prototyper/wizard/__init__.py b/module_prototyper/wizard/__init__.py new file mode 100644 index 000000000..bb0eb2ebe --- /dev/null +++ b/module_prototyper/wizard/__init__.py @@ -0,0 +1,22 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2010 - 2014 Savoir-faire Linux +# (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +from . import module_prototyper_module_export diff --git a/module_prototyper/wizard/module_prototyper_module_export.py b/module_prototyper/wizard/module_prototyper_module_export.py new file mode 100644 index 000000000..92416fbdf --- /dev/null +++ b/module_prototyper/wizard/module_prototyper_module_export.py @@ -0,0 +1,121 @@ +# -*- encoding: utf-8 -*- +# ############################################################################# +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2010 - 2014 Savoir-faire Linux +# (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +import StringIO +import base64 +import zipfile +from collections import namedtuple +from openerp import fields, models, api + + +class PrototypeModuleExport(models.TransientModel): + _name = "module_prototyper.module.export" + + name = fields.Char('File Name', readonly=True) + # It is implemented in order to manage previous and next versions + # of odoo + api_version = fields.Selection( + [ + ('8.0', '8.0'), + # ('7.0', '7.0') + ], + 'API version', + required=True, + default='8.0' + ) + data = fields.Binary('File', readonly=True) + state = fields.Selection( + [ + ('choose', 'choose'), # choose version + ('get', 'get') # get module + ], + default='choose' + ) + + @api.model + def action_export(self, ids): + """ + Export a zip file containing the module based on the information + provided in the prototype, using the templates chosen in the wizard. + """ + if isinstance(ids, (int, long)): + ids = [ids] + wizard = self.browse(ids) + + active_model = self._context.get('active_model') + + # checking if the wizard was called by a prototype. + msg = '{} has to be called from a "module_prototyper" , not a "{}"' + assert active_model == 'module_prototyper', msg.format( + self, active_model + ) + + # getting the prototype of the wizard + prototype = self.env[active_model].browse( + self._context.get('active_id') + ) + + # setting the jinja environment. + # They will help the program to find the template to render the files + # with. + prototype.set_jinja_env(wizard.api_version) + + # generate_files ask the prototype to investigate the input + # and to generate the file templates according to it. + # zip_files, in another hand, put all the template files into a package + # ready to be saved by the user. + zip_details = self.zip_files(prototype.generate_files()) + + wizard.write( + { + 'name': '{}.zip'.format(prototype.name), + 'state': 'get', + 'data': base64.encodestring(zip_details.stringIO.getvalue()) + } + ) + + return { + 'type': 'ir.actions.act_window', + 'res_model': 'module_prototyper.module.export', + 'view_mode': 'form', + 'view_type': 'form', + 'res_id': wizard.id, + 'views': [(False, 'form')], + 'target': 'new', + } + + @staticmethod + def zip_files(file_details): + """Takes a set of file and zips them. + :param file_details: tuple (filename, file_content) + :return: tuple (zip_file, stringIO) + """ + zip_details = namedtuple('Zip_details', ['zip_file', 'stringIO']) + out = StringIO.StringIO() + + with zipfile.ZipFile(out, 'w') as target: + for filename, file_content in file_details: + info = zipfile.ZipInfo(filename) + info.compress_type = zipfile.ZIP_DEFLATED + info.external_attr = 2175008768 # specifies mode 0644 + target.writestr(info, file_content) + + return zip_details(zip_file=target, stringIO=out) diff --git a/module_prototyper/wizard/module_prototyper_module_export_view.xml b/module_prototyper/wizard/module_prototyper_module_export_view.xml new file mode 100644 index 000000000..d4678ebf5 --- /dev/null +++ b/module_prototyper/wizard/module_prototyper_module_export_view.xml @@ -0,0 +1,43 @@ + + + + + + Export Module + module_prototyper.module.export + +
+ + + + + + + + + +
+

Export Complete

+

Here is the exported module:

+
+
+
+
+
+ +
+
+ + + Export Module + module_prototyper.module.export + form + + new + + +
+