Browse Source

[IMP] prototype: implementation of jinja syntax.

[IMP] prototype: Switched to v8 api.

[IMP] prototype: renamed the templates to avoid confusion with pep8 tests.

[IMP] prototype: renamed some fields in prototype.py.

[IMP] prototype: implemented the process to create the zip file to export.

[IMP] prototype: worked on the generation of __openerp__.py and __init__.py files from the prototype.

[IMP] prototype: pep8

[IMP] prototype: added more comments.

[IMP] prototype: worked on fields.

[IMP] prototype: implemented the generation of files from models/model_name.py.template and models/init.py.template

[IMP] prototype: first work on implementation of generations of views.

[IMP] prototype: improved the arch section in model_view.xml.template

[IMP] prototype: Reviewed the names of many2many from plurials to name_ids.

[IMP] prototype: added menus.

[IMP] prototype: typo in templates/8.0/views/model_view.xml.template

[IMP] prototype: improved template with adding extension to header.template to ease management of file with licence header.

[IMP] prototype: prototype.prototype demo data

[IMP] prototype: worked to get the views and menus properly generated. Added actions to model_menus.xml.template.

[IMP] prototype: should fix pep8 in model_name.py.template.
pull/107/head
Jordi Riera 10 years ago
committed by Maxime Chambreuil
parent
commit
69db83fc87
  1. 8
      prototype/__openerp__.py
  2. 2
      prototype/demo/prototype.prototype.csv
  3. 4
      prototype/i18n/fr.po
  4. 4
      prototype/i18n/prototype.pot
  5. 8
      prototype/models/ir_model_fields.py
  6. 358
      prototype/models/prototype.py
  7. 50
      prototype/templates/7.0/__openerp__.py
  8. 8
      prototype/templates/7.0/data/model_name.xml
  9. 8
      prototype/templates/7.0/demo/model_name.xml
  10. 24
      prototype/templates/7.0/models/__init__.py
  11. 36
      prototype/templates/7.0/models/model_name.py
  12. 1
      prototype/templates/7.0/security/ir.model.access.csv
  13. 6
      prototype/templates/8.0/__init__.py.template
  14. 50
      prototype/templates/8.0/__openerp__.py
  15. 38
      prototype/templates/8.0/__openerp__.py.template
  16. 2
      prototype/templates/8.0/data/model_name.xml.template
  17. 8
      prototype/templates/8.0/demo/model_name.xml
  18. 2
      prototype/templates/8.0/demo/model_name.xml.template
  19. 6
      prototype/templates/8.0/header.template
  20. 24
      prototype/templates/8.0/models/__init__.py
  21. 6
      prototype/templates/8.0/models/__init__.py.template
  22. 34
      prototype/templates/8.0/models/model_name.py
  23. 21
      prototype/templates/8.0/models/model_name.py.template
  24. 1
      prototype/templates/8.0/security/ir.model.access.csv
  25. 4
      prototype/templates/8.0/security/ir.model.access.csv.template
  26. 10
      prototype/templates/8.0/security/model_name.xml
  27. 4
      prototype/templates/8.0/security/model_name.xml.template
  28. 24
      prototype/templates/8.0/views/model_menus.xml.template
  29. 8
      prototype/templates/8.0/views/model_view.xml
  30. 23
      prototype/templates/8.0/views/model_views.xml.template
  31. 14
      prototype/tests/__init__.py
  32. 83
      prototype/tests/test_prototype.py
  33. 83
      prototype/tests/test_prototype_module_export.py
  34. 184
      prototype/views/prototype_view.xml
  35. 120
      prototype/wizard/prototype_module_export.py
  36. 2
      prototype/wizard/prototype_module_export_view.xml

8
prototype/__openerp__.py

@ -45,10 +45,10 @@ Contributors
* Maxime Chambreuil <maxime.chambreuil@savoirfairelinux.com> * Maxime Chambreuil <maxime.chambreuil@savoirfairelinux.com>
* Savoir-faire Linux <support@savoirfairelinux.com> * Savoir-faire Linux <support@savoirfairelinux.com>
More informations
-----------------
* Module developped and tested with Odoo version 8.0
* For questions, please contact our support services \
More information
----------------
* Module developed and tested with Odoo version 8.0
* For questions, please contact our support services
(support@savoirfairelinux.com) (support@savoirfairelinux.com)
""", """,
'depends': [ 'depends': [

2
prototype/demo/prototype.prototype.csv
File diff suppressed because it is too large
View File

4
prototype/i18n/fr.po

@ -87,7 +87,7 @@ msgid "Demo filters"
msgstr "Filtres pour les données de démo" msgstr "Filtres pour les données de démo"
#. module: prototype #. module: prototype
#: view:prototype:prototype.view_prototype_form field:prototype,depends:0
#: view:prototype:prototype.view_prototype_form field:prototype,dependencies:0
msgid "Dependencies" msgid "Dependencies"
msgstr "Dépendances" msgstr "Dépendances"
@ -190,7 +190,7 @@ msgid "Module"
msgstr "" msgstr ""
#. module: prototype #. module: prototype
#: field:prototype,shortdesc:0
#: field:prototype,human_name:0
msgid "Module Name" msgid "Module Name"
msgstr "Nom du module" msgstr "Nom du module"

4
prototype/i18n/prototype.pot

@ -87,7 +87,7 @@ msgid "Demo filters"
msgstr "" msgstr ""
#. module: prototype #. module: prototype
#: view:prototype:prototype.view_prototype_form field:prototype,depends:0
#: view:prototype:prototype.view_prototype_form field:prototype,dependencies:0
msgid "Dependencies" msgid "Dependencies"
msgstr "" msgstr ""
@ -190,7 +190,7 @@ msgid "Module"
msgstr "" msgstr ""
#. module: prototype #. module: prototype
#: field:prototype,shortdesc:0
#: field:prototype,human_name:0
msgid "Module Name" msgid "Module Name"
msgstr "" msgstr ""

8
prototype/models/ir_model_fields.py

@ -19,12 +19,10 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
############################################################################## ##############################################################################
from openerp.osv import fields, osv
from openerp import fields, models
class ir_model_fields(osv.osv):
class ir_model_fields(models.Model):
_inherit = "ir.model.fields" _inherit = "ir.model.fields"
_columns = {
'notes': fields.text('Notes'),
}
notes = fields.Text('Notes')

358
prototype/models/prototype.py

@ -1,5 +1,5 @@
# -*- encoding: utf-8 -*- # -*- encoding: utf-8 -*-
##############################################################################
# #############################################################################
# #
# OpenERP, Open Source Management Solution # OpenERP, Open Source Management Solution
# This module copyright (C) 2010 - 2014 Savoir-faire Linux # This module copyright (C) 2010 - 2014 Savoir-faire Linux
@ -19,51 +19,321 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
############################################################################## ##############################################################################
from openerp.osv import fields, osv
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 prototype(osv.osv):
class Prototype(models.Model):
_name = "prototype" _name = "prototype"
_description = "Prototype" _description = "Prototype"
_columns = {
'name': fields.char('Technical Name', required=True),
'category_id': fields.many2one('ir.module.category', 'Category'),
'shortdesc': fields.char('Module Name', required=True),
'summary': fields.char('Summary', required=True),
'description': fields.html('Description', required=True),
'author': fields.char('Author', required=True),
'maintainer': fields.char('Maintainer'),
'website': fields.char('Website'),
'icon_image': fields.binary('Icon'),
'version': fields.char('Version', size=3),
'auto_install': fields.boolean('Auto Install'),
# Relations
'depends': fields.many2many('ir.module.module', 'prototype_module_rel',
'prototype_id', 'module_id',
'Dependencies'),
'data': fields.many2many('ir.filters', 'prototype_data_rel',
'prototype_id', 'filter_id',
'Data filters', help="The records matching the filters will be added as data."),
'demo': fields.many2many('ir.filters', 'prototype_demo_rel',
'prototype_id', 'filter_id',
'Demo filters', help="The records matching the filters will be added as demo data."),
'fields': fields.many2many('ir.model.fields', 'prototype_fields_rel',
'prototype_id', 'field_id', 'Fields'),
'menu': fields.many2many('ir.ui.menu', 'prototype_menu_rel',
'prototype_id', 'menu_id', 'Menu Items'),
'views': fields.many2many('ir.ui.view', 'prototype_view_rel',
'prototype_id', 'view_id', 'Views'),
'groups': fields.many2many('res.groups', 'prototype_groups_rel',
'prototype_id', 'group_id', 'Groups'),
'rights': fields.many2many('ir.model.access', 'prototype_rights_rel',
'prototype_id', 'right_id',
'Access Rights'),
'rules': fields.many2many('ir.rule', 'prototype_rule_rel',
'prototype_id', 'rule_id', 'Record Rules'),
}
_defaults = {
'auto_install': False,
'version': '0.1',
}
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', 'prototype_module_rel',
'prototype_id', 'module_id',
'Dependencies'
)
data_ids = fields.Many2many(
'ir.filters',
'prototype_data_rel',
'prototype_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',
'prototype_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',
'prototype_id', 'field_id', 'Fields'
)
menu_ids = fields.Many2many(
'ir.ui.menu', 'prototype_menu_rel',
'prototype_id', 'menu_id', 'Menu Items'
)
view_ids = fields.Many2many(
'ir.ui.view', 'prototype_view_rel',
'prototype_id', 'view_id', 'Views'
)
group_ids = fields.Many2many(
'res.groups', 'prototype_groups_rel',
'prototype_id', 'group_id', 'Groups'
)
right_ids = fields.Many2many(
'ir.model.access', 'prototype_rights_rel',
'prototype_id', 'right_id',
'Access Rights'
)
rule_ids = fields.Many2many(
'ir.rule', 'prototype_rule_rel',
'prototype_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())
file_details.append(self.save_icon())
return file_details
@api.model
def save_icon(self):
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, fields in relations.iteritems():
files.append(self.generate_model_details(model, 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))

50
prototype/templates/7.0/__openerp__.py

@ -1,50 +0,0 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# Odoo, Open Source Management Solution
# This module copyright (C) $export_date $author
# (<$website>).
#
# 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 <http://www.gnu.org/licenses/>.
#
##############################################################################
{
'name': '$name',
'version': '$version',
'author': '$author',
'maintainer': '$maintainer',
'website': '$website',
'license': 'AGPL-3',
'category': '$category',
'summary': '$summary',
'description': """
$description
* Module exported by the prototype module for version 7.0.
* If you have any questions, please contact Savoir-faire Linux \
(support@savoirfairelinux.com)
""",
'depends': [
$depends
],
'external_dependencies': {
'python': [],
},
'data': [
$data
],
'installable': True,
'auto_install': $auto_install,
}

8
prototype/templates/7.0/data/model_name.xml

@ -1,8 +0,0 @@
<?xml version="1.0"?>
<openerp>
<data>
$data
</data>
</openerp>

8
prototype/templates/7.0/demo/model_name.xml

@ -1,8 +0,0 @@
<?xml version="1.0"?>
<openerp>
<data>
$demo
</data>
</openerp>

24
prototype/templates/7.0/models/__init__.py

@ -1,24 +0,0 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# Odoo, Open Source Management Solution
# This module copyright (C) $generation_date $author
# (<$website>).
#
# 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 <http://www.gnu.org/licenses/>.
#
##############################################################################
from . import (
$models.py
)

36
prototype/templates/7.0/models/model_name.py

@ -1,36 +0,0 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# Odoo, Open Source Management Solution
# This module copyright (C) $generation_date $author
# (<$website>).
#
# 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 <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp.osv import fields, osv
class $model_name(osv.osv):
_name = "$model_name"
_inherit = "$model_name"
_description = "$model_description"
_columns = {
$model_fields
}
_defaults = {
}

1
prototype/templates/7.0/security/ir.model.access.csv

@ -1 +0,0 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink

6
prototype/templates/8.0/__init__.py.template

@ -0,0 +1,6 @@
{% extends "header.template" %}
{% block body %}
{% if models -%}
from . import models
{% endif %}
{% endblock %}

50
prototype/templates/8.0/__openerp__.py

@ -1,50 +0,0 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# Odoo, Open Source Management Solution
# This module copyright (C) $export_date $author
# (<$website>).
#
# 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 <http://www.gnu.org/licenses/>.
#
##############################################################################
{
'name': '$name',
'version': '$version',
'author': '$author',
'maintainer': '$maintainer',
'website': '$website',
'license': 'AGPL-3',
'category': '$category',
'summary': '$summary',
'description': """
$description
* Module exported by the prototype module for version 8.0.
* If you have any questions, please contact Savoir-faire Linux \
(support@savoirfairelinux.com)
""",
'depends': [
$depends
],
'external_dependencies': {
'python': [],
},
'data': [
$data
],
'installable': True,
'auto_install': $auto_install,
}

38
prototype/templates/8.0/__openerp__.py.template

@ -0,0 +1,38 @@
{% extends "header.template" %}
{% block body %}
{
'name': '{{ prototype.name }}',
'version': '{{ prototype.version }}',
'author': '{{ prototype.author }}',
'maintainer': '{{ prototype.maintainer }}',
'website': '{{ prototype.website }}',
'license': '{{ prototype.licence }}',
'category': '{{ prototype.category_id.name }}',
'summary': '{{ prototype.summary }}',
'description': """
{{ prototype.description }}
* Module exported by the prototype module for version 8.0.
* If you have any questions, please contact Savoir-faire Linux \
(support@savoirfairelinux.com)
""",
'depends': [
{% for dependency in prototype.dependency_ids -%}
'{{ dependency.name }}',
{% endfor -%}],
'external_dependencies': {
'python': [],
},
'data': [
{% for data_file in data_files -%}
'{{ data_file }}',
{% endfor -%}],
'demo': [
{% for demo_file in prototype.demo_ids -%}
'{{ demo_file.name }}',
{% endfor -%}],
'installable': True,
'auto_install': {{ prototype.auto_install }},
'application': {{ prototype.application }},
}
{% endblock %}

2
prototype/templates/8.0/data/model_name.xml → prototype/templates/8.0/data/model_name.xml.template

@ -2,7 +2,7 @@
<openerp> <openerp>
<data> <data>
$data
{{ data }}
</data> </data>
</openerp> </openerp>

8
prototype/templates/8.0/demo/model_name.xml

@ -1,8 +0,0 @@
<?xml version="1.0"?>
<openerp>
<data>
$demo
</data>
</openerp>

2
prototype/templates/7.0/views/model_name_view.xml → prototype/templates/8.0/demo/model_name.xml.template

@ -2,7 +2,7 @@
<openerp> <openerp>
<data> <data>
$views
{{ demo }}
</data> </data>
</openerp> </openerp>

6
prototype/templates/7.0/__init__.py → prototype/templates/8.0/header.template

@ -2,8 +2,8 @@
############################################################################## ##############################################################################
# #
# Odoo, Open Source Management Solution # Odoo, Open Source Management Solution
# This module copyright (C) $generation_date $author
# (<$website>).
# This module copyright (C) {{ export_year }} {{ author }}
# ({{ website }}).
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as # it under the terms of the GNU Affero General Public License as
@ -19,4 +19,4 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
############################################################################## ##############################################################################
from . import models
{% block body %}{% endblock %}

24
prototype/templates/8.0/models/__init__.py

@ -1,24 +0,0 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# Odoo, Open Source Management Solution
# This module copyright (C) $generation_date $author
# (<$website>).
#
# 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 <http://www.gnu.org/licenses/>.
#
##############################################################################
from . import (
$models.py
)

6
prototype/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 %}

34
prototype/templates/8.0/models/model_name.py

@ -1,34 +0,0 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# Odoo, Open Source Management Solution
# This module copyright (C) $generation_date $author
# (<$website>).
#
# 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 <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp.osv import fields, osv
class $model_name(osv.osv):
_name = "$model_name"
_inherit = "$model_name"
_description = "$model_description"
$model_fields
_defaults = {
}

21
prototype/templates/8.0/models/model_name.py.template

@ -0,0 +1,21 @@
{% 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 -%}
{{ field.name }} = fields.{{ field.ttype|capitalize }}(
string=_("{{ field.field_description }}"),
required={{ field.required }},
translate={{ field.translate }},
readonly={{ field.readonly }},
help=_('{{ field.notes }}'),
)
{% endfor %}
{% endblock %}

1
prototype/templates/8.0/security/ir.model.access.csv

@ -1 +0,0 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink

4
prototype/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 %}

10
prototype/templates/8.0/security/model_name.xml

@ -1,10 +0,0 @@
<?xml version="1.0"?>
<openerp>
<data>
$groups
$rules
</data>
</openerp>

4
prototype/templates/7.0/security/model_name.xml → prototype/templates/8.0/security/model_name.xml.template

@ -2,9 +2,9 @@
<openerp> <openerp>
<data> <data>
$groups
{{ groups }}
$rules
{{ rules }}
</data> </data>
</openerp> </openerp>

24
prototype/templates/8.0/views/model_menus.xml.template

@ -0,0 +1,24 @@
<?xml version="1.0"?>
<openerp>
<data>
{% for menu in menus -%}
<record id="action_{{ menu.action.name }}_{{ menu.action.view_type }}_view" model="{{ menu.action.type }}">
<field name="name">{{ menu.action.name }}</field>
<field name="type">{{ menu.action.type }}</field>
<field name="res_model">{{ menu.action.res_model }}</field>
<field name="view_type">{{ menu.action.view_type }}</field>
<field name="view_mode">{{ menu.action.view_mode }}</field>
{% if menu.action.help %}<field name="help" type="html">{{ menu.action.help }}
</field>{% endif %}
</record>
<menuitem action="action_{{ menu.action.name }}_{{ menu.action.view_type }}_view"
name="{{ menu.name }}"
id="menu_action_{{ menu.name|replace('.', '_') }}_{{ menu.action.view_type }}"
{% if menu.parent_id %}parent="{{ menu.parent_id.get_xml_id(cr,1,1).values()[0] }}"{% endif %}
sequence="{{ menu.sequence }}"
groups="{% for group in menu.groups_id %}{{ group.get_xml_id(cr,1,1).values()[0] }},{% endfor %}"
/>
{% endfor -%}
</data>
</openerp>

8
prototype/templates/8.0/views/model_view.xml

@ -1,8 +0,0 @@
<?xml version="1.0"?>
<openerp>
<data>
$views
</data>
</openerp>

23
prototype/templates/8.0/views/model_views.xml.template

@ -0,0 +1,23 @@
<?xml version="1.0"?>
<openerp>
<data>
<!-- TODO: put here a reminder on what to do at the first edition -->
{% for view in views %}
<record id="{{ view.name|replace('.', '_')}}_view" model="ir.ui.model">
<field name="name">{{ view.name }}.view</field>
<field name="model">{{ view.model }}</field>
<field name="view_type">{{ view.type }}</field>
<field name="inherit_id" ref="{{ view.inherit_id.get_xml_id(cr,1,1).values()[0] }}"/>
<field name="arch" type="xml">
{# 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('<?xml version="1.0"?>', '')|replace('"x_', '"') }}
</field>
</record>
{% endfor %}
</data>
</openerp>

14
prototype/templates/8.0/__init__.py → prototype/tests/__init__.py

@ -1,9 +1,9 @@
# -*- encoding: utf-8 -*- # -*- encoding: utf-8 -*-
############################################################################## ##############################################################################
# #
# Odoo, Open Source Management Solution
# This module copyright (C) $generation_date $author
# (<$website>).
# OpenERP, Open Source Management Solution
# This module copyright (C) 2010 - 2014 Savoir-faire Linux
# (<http://www.savoirfairelinux.com>).
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as # it under the terms of the GNU Affero General Public License as
@ -19,4 +19,10 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
############################################################################## ##############################################################################
from . import models
from . import test_prototype_module_export
from . import test_prototype
checks = [
test_prototype_module_export,
test_prototype,
]

83
prototype/tests/test_prototype.py

@ -0,0 +1,83 @@
# -*- encoding: utf-8 -*- #
# OpenERP, Open Source Management Solution
# This module copyright (C) 2013 Savoir-faire Linux
# (<http://www.savoirfairelinux.com>).
#
# 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 <http://www.gnu.org/licenses/>.
#
from jinja2 import Environment
from jinja2.exceptions import TemplateNotFound
from openerp.tests import common
class test_prototype(common.TransactionCase):
def setUp(self):
super(test_prototype, self).setUp()
self.main_model = self.env['prototype']
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('.', '_')
)

83
prototype/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
# (<http://www.savoirfairelinux.com>).
#
# 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 <http://www.gnu.org/licenses/>.
#
##############################################################################
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['prototype.module.export']
self.prototype_model = self.env['prototype']
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
)

184
prototype/views/prototype_view.xml

@ -1,97 +1,103 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<openerp> <openerp>
<data>
<data>
<record id="view_prototype_tree" model="ir.ui.view">
<field name="name">prototype.tree</field>
<field name="model">prototype</field>
<field name="arch" type="xml">
<tree string="Prototype">
<field name="name"/>
<field name="shortdesc"/>
<field name="summary"/>
</tree>
</field>
</record>
<record id="view_prototype_tree" model="ir.ui.view">
<field name="name">prototype.tree</field>
<field name="model">prototype</field>
<field name="arch" type="xml">
<tree string="Prototype">
<field name="human_name"/>
<field name="name"/>
<field name="summary"/>
</tree>
</field>
</record>
<record id="view_prototype_form" model="ir.ui.view">
<field name="name">prototype.form</field>
<field name="model">prototype</field>
<field name="arch" type="xml">
<form string="Module">
<link rel="stylesheet" href="/base/static/src/css/description.css"/>
<sheet>
<field name="icon_image" widget="image" class="oe_avatar oe_left"/>
<div class="oe_title">
<h1><field name="summary"/></h1>
<div>
<button name="%(button_module_export_action)d" string="Export" type="action"/>
</div>
</div>
<group>
<group>
<field name="name"/>
<field name="shortdesc"/>
<field name="category_id"/>
<field name="version"/>
</group>
<group>
<field name="author"/>
<field name="website" widget="url"/>
<field name="maintainer"/>
<field name="auto_install"/>
</group>
</group>
<notebook>
<page string="Description">
<field name="description"/>
</page>
<page string="Dependencies">
<field name="depends"/>
</page>
<page string="Data &amp; Demo">
<label for="data"/>
<field name="data"/>
<label for="demo"/>
<field name="demo"/>
</page>
<page string="Models">
<label for="fields"/>
<field name="fields" domain="[('state', '=', 'manual')]"/>
</page>
<page string="Interface">
<label for="menu"/>
<field name="menu"/>
<label for="views"/>
<field name="views"/>
</page>
<page string="Security">
<label for="groups"/>
<field name="groups"/>
<label for="rights"/>
<field name="rights"/>
<label for="rules"/>
<field name="rules"/>
</page>
</notebook>
</sheet>
</form>
</field>
</record>
<record id="view_prototype_form" model="ir.ui.view">
<field name="name">prototype.form</field>
<field name="model">prototype</field>
<field name="arch" type="xml">
<form string="Module">
<link rel="stylesheet"
href="/base/static/src/css/description.css"/>
<sheet>
<field name="icon_image" widget="image"
class="oe_avatar oe_left"/>
<div class="oe_title">
<h1>
<field name="human_name"/>
</h1>
<div>
<button name="%(button_module_export_action)d"
string="Export" type="action"/>
</div>
</div>
<group>
<group>
<field name="name"/>
<field name="summary"/>
<field name="category_id"/>
<field name="version"/>
</group>
<group>
<field name="author"/>
<field name="website" widget="url"/>
<field name="maintainer"/>
<field name="auto_install"/>
<field name="application"/>
</group>
</group>
<notebook>
<page string="Description">
<field name="description"/>
</page>
<page string="Dependencies">
<field name="dependency_ids"/>
</page>
<page string="Data &amp; Demo">
<label for="data_ids"/>
<field name="data_ids"/>
<label for="demo_ids"/>
<field name="demo_ids"/>
</page>
<page string="Fields">
<label for="field_ids"/>
<field name="field_ids"/>
</page>
<page string="Interface">
<label for="menu_ids"/>
<field name="menu_ids"/>
<label for="view_ids"/>
<field name="view_ids"/>
</page>
<page string="Security">
<label for="group_ids"/>
<field name="group_ids"/>
<label for="right_ids"/>
<field name="right_ids"/>
<label for="rule_ids"/>
<field name="rule_ids"/>
</page>
</notebook>
</sheet>
</form>
</field>
</record>
<record id="open_prototype_list" model="ir.actions.act_window">
<field name="name">Prototype</field>
<field name="res_model">prototype</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="view_id" ref="view_prototype_tree"/>
</record>
<record id="open_prototype_list" model="ir.actions.act_window">
<field name="name">Prototype</field>
<field name="res_model">prototype</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="view_id" ref="view_prototype_tree"/>
</record>
<menuitem id="menu_open_prototype"
action="open_prototype_list"
parent="base.menu_management"
sequence="30"
groups="base.group_system"/>
<menuitem id="menu_open_prototype"
action="open_prototype_list"
parent="base.menu_management"
sequence="30"
groups="base.group_system"/>
</data>
</data>
</openerp> </openerp>

120
prototype/wizard/prototype_module_export.py

@ -1,12 +1,12 @@
# -*- encoding: utf-8 -*- # -*- encoding: utf-8 -*-
##############################################################################
# #############################################################################
# #
# OpenERP, Open Source Management Solution
# This module copyright (C) 2010 - 2014 Savoir-faire Linux
# (<http://www.savoirfairelinux.com>).
# OpenERP, Open Source Management Solution
# This module copyright (C) 2010 - 2014 Savoir-faire Linux
# (<http://www.savoirfairelinux.com>).
# #
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# 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 # published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version. # License, or (at your option) any later version.
# #
@ -19,49 +19,103 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
############################################################################## ##############################################################################
from openerp.osv import fields,osv
from openerp.tools.translate import _
import StringIO
import base64
import zipfile
from collections import namedtuple
from openerp import fields, models, api
class prototype_module_export(osv.osv_memory):
class prototype_module_export(models.TransientModel):
_name = "prototype.module.export" _name = "prototype.module.export"
_columns = {
'name': fields.char('File Name', readonly=True),
'api_version': fields.selection([('8.0','8.0'),
('7.0', '7.0')], 'API version',
required=True),
'data': fields.binary('File', readonly=True),
'state': fields.selection([('choose', 'choose'), # choose version
('get', 'get')]) # get module
}
_defaults = {
'state': 'choose',
'api_version': '8.0',
}
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'
)
def act_getfile(self, cr, uid, ids, context=None):
@api.model
def action_export(self, ids):
""" """
Export a zip file containing the module based on the information Export a zip file containing the module based on the information
provided in the prototype, using the templates chosen in the wizard. provided in the prototype, using the templates chosen in the wizard.
""" """
this = self.browse(cr, uid, ids)[0]
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.
assert active_model == 'prototype', \
'{} has to be called from a "prototype" , not a "{}"'.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)
# TODO: Implement the export logic here
filename = 'new'
this.name = "%s.%s" % (filename, 'zip')
out = 'toto'
# /TODO
# 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())
}
)
self.write(cr, uid, ids, {'state': 'get',
'data': out,
'name':this.name}, context=context)
return { return {
'type': 'ir.actions.act_window', 'type': 'ir.actions.act_window',
'res_model': 'prototype.module.export', 'res_model': 'prototype.module.export',
'view_mode': 'form', 'view_mode': 'form',
'view_type': 'form', 'view_type': 'form',
'res_id': this.id,
'res_id': wizard.id,
'views': [(False, 'form')], 'views': [(False, 'form')],
'target': 'new', 'target': 'new',
} }
@staticmethod
def zip_files(file_details):
"""Takes a set of file and zips them.
:param file_details: tuple (filename, filecontent)
: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, filecontent in file_details:
info = zipfile.ZipInfo(filename)
info.compress_type = zipfile.ZIP_DEFLATED
info.external_attr = 2175008768 # specifies mode 0644
target.writestr(info, filecontent)
return zip_details(zip_file=target, stringIO=out)

2
prototype/wizard/prototype_module_export_view.xml

@ -21,7 +21,7 @@
<p>Here is the exported module: <field name="data" readonly="1" filename="name"/></p> <p>Here is the exported module: <field name="data" readonly="1" filename="name"/></p>
</div> </div>
<footer states="choose"> <footer states="choose">
<button name="act_getfile" string="Export" type="object" class="oe_highlight"/> or
<button name="action_export" string="Export" type="object" class="oe_highlight"/> or
<button special="cancel" string="Cancel" type="object" class="oe_link"/> <button special="cancel" string="Cancel" type="object" class="oe_link"/>
</footer> </footer>
<footer states="get"> <footer states="get">

Loading…
Cancel
Save