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. 342
      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. 54
      prototype/views/prototype_view.xml
  35. 108
      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>
* 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)
""",
'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"
#. module: prototype
#: view:prototype:prototype.view_prototype_form field:prototype,depends:0
#: view:prototype:prototype.view_prototype_form field:prototype,dependencies:0
msgid "Dependencies"
msgstr "Dépendances"
@ -190,7 +190,7 @@ msgid "Module"
msgstr ""
#. module: prototype
#: field:prototype,shortdesc:0
#: field:prototype,human_name:0
msgid "Module Name"
msgstr "Nom du module"

4
prototype/i18n/prototype.pot

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

8
prototype/models/ir_model_fields.py

@ -19,12 +19,10 @@
# 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"
_columns = {
'notes': fields.text('Notes'),
}
notes = fields.Text('Notes')

342
prototype/models/prototype.py

@ -19,51 +19,321 @@
# 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"
_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'),
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
'depends': fields.many2many('ir.module.module', 'prototype_module_rel',
dependency_ids = fields.Many2many(
'ir.module.module', 'prototype_module_rel',
'prototype_id', 'module_id',
'Dependencies'),
'data': fields.many2many('ir.filters', 'prototype_data_rel',
'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': fields.many2many('ir.filters', 'prototype_demo_rel',
'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."),
'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',
'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'),
'rules': fields.many2many('ir.rule', 'prototype_rule_rel',
'prototype_id', 'rule_id', 'Record Rules'),
}
'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('.', '_')
_defaults = {
'auto_install': False,
'version': '0.1',
@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>
<data>
$data
{{ data }}
</data>
</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>
<data>
$views
{{ demo }}
</data>
</openerp>

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

@ -2,8 +2,8 @@
##############################################################################
#
# 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
# 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/>.
#
##############################################################################
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>
<data>
$groups
{{ groups }}
$rules
{{ rules }}
</data>
</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 -*-
##############################################################################
#
# 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
# 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/>.
#
##############################################################################
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
)

54
prototype/views/prototype_view.xml

@ -7,8 +7,8 @@
<field name="model">prototype</field>
<field name="arch" type="xml">
<tree string="Prototype">
<field name="human_name"/>
<field name="name"/>
<field name="shortdesc"/>
<field name="summary"/>
</tree>
</field>
@ -19,19 +19,24 @@
<field name="model">prototype</field>
<field name="arch" type="xml">
<form string="Module">
<link rel="stylesheet" href="/base/static/src/css/description.css"/>
<link rel="stylesheet"
href="/base/static/src/css/description.css"/>
<sheet>
<field name="icon_image" widget="image" class="oe_avatar oe_left"/>
<field name="icon_image" widget="image"
class="oe_avatar oe_left"/>
<div class="oe_title">
<h1><field name="summary"/></h1>
<h1>
<field name="human_name"/>
</h1>
<div>
<button name="%(button_module_export_action)d" string="Export" type="action"/>
<button name="%(button_module_export_action)d"
string="Export" type="action"/>
</div>
</div>
<group>
<group>
<field name="name"/>
<field name="shortdesc"/>
<field name="summary"/>
<field name="category_id"/>
<field name="version"/>
</group>
@ -40,6 +45,7 @@
<field name="website" widget="url"/>
<field name="maintainer"/>
<field name="auto_install"/>
<field name="application"/>
</group>
</group>
<notebook>
@ -47,31 +53,31 @@
<field name="description"/>
</page>
<page string="Dependencies">
<field name="depends"/>
<field name="dependency_ids"/>
</page>
<page string="Data &amp; Demo">
<label for="data"/>
<field name="data"/>
<label for="demo"/>
<field name="demo"/>
<label for="data_ids"/>
<field name="data_ids"/>
<label for="demo_ids"/>
<field name="demo_ids"/>
</page>
<page string="Models">
<label for="fields"/>
<field name="fields" domain="[('state', '=', 'manual')]"/>
<page string="Fields">
<label for="field_ids"/>
<field name="field_ids"/>
</page>
<page string="Interface">
<label for="menu"/>
<field name="menu"/>
<label for="views"/>
<field name="views"/>
<label for="menu_ids"/>
<field name="menu_ids"/>
<label for="view_ids"/>
<field name="view_ids"/>
</page>
<page string="Security">
<label for="groups"/>
<field name="groups"/>
<label for="rights"/>
<field name="rights"/>
<label for="rules"/>
<field name="rules"/>
<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>

108
prototype/wizard/prototype_module_export.py

@ -19,49 +19,103 @@
# 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"
_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
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 {
'type': 'ir.actions.act_window',
'res_model': 'prototype.module.export',
'view_mode': 'form',
'view_type': 'form',
'res_id': this.id,
'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, 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>
</div>
<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"/>
</footer>
<footer states="get">

Loading…
Cancel
Save