[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
parent
75d00737c9
commit
69db83fc87
|
@ -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
|
More information
|
||||||
-----------------
|
----------------
|
||||||
* Module developped and tested with Odoo version 8.0
|
* Module developed and tested with Odoo version 8.0
|
||||||
* For questions, please contact our support services \
|
* For questions, please contact our support services
|
||||||
(support@savoirfairelinux.com)
|
(support@savoirfairelinux.com)
|
||||||
""",
|
""",
|
||||||
'depends': [
|
'depends': [
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -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"
|
||||||
|
|
||||||
|
|
|
@ -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 ""
|
||||||
|
|
||||||
|
|
|
@ -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'),
|
|
||||||
}
|
|
||||||
|
|
|
@ -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 = {
|
licence = fields.Char(
|
||||||
'name': fields.char('Technical Name', required=True),
|
'Licence',
|
||||||
'category_id': fields.many2one('ir.module.category', 'Category'),
|
default='AGPL-3',
|
||||||
'shortdesc': fields.char('Module Name', required=True),
|
)
|
||||||
'summary': fields.char('Summary', required=True),
|
name = fields.Char(
|
||||||
'description': fields.html('Description', required=True),
|
'Technical Name', required=True,
|
||||||
'author': fields.char('Author', required=True),
|
help=('The technical name will be used to define the name of '
|
||||||
'maintainer': fields.char('Maintainer'),
|
'the exported module, the name of the model.')
|
||||||
'website': fields.char('Website'),
|
)
|
||||||
'icon_image': fields.binary('Icon'),
|
category_id = fields.Many2one('ir.module.category', 'Category')
|
||||||
'version': fields.char('Version', size=3),
|
human_name = fields.Char(
|
||||||
'auto_install': fields.boolean('Auto Install'),
|
'Module Name', required=True,
|
||||||
# Relations
|
help=('The Module Name will be used as the displayed name of the '
|
||||||
'depends': fields.many2many('ir.module.module', 'prototype_module_rel',
|
'exported module.')
|
||||||
'prototype_id', 'module_id',
|
)
|
||||||
'Dependencies'),
|
summary = fields.Char('Summary', required=True)
|
||||||
'data': fields.many2many('ir.filters', 'prototype_data_rel',
|
description = fields.Text('Description', required=True)
|
||||||
'prototype_id', 'filter_id',
|
author = fields.Char('Author', required=True)
|
||||||
'Data filters', help="The records matching the filters will be added as data."),
|
maintainer = fields.Char('Maintainer')
|
||||||
'demo': fields.many2many('ir.filters', 'prototype_demo_rel',
|
website = fields.Char('Website')
|
||||||
'prototype_id', 'filter_id',
|
icon_image = fields.Binary(
|
||||||
'Demo filters', help="The records matching the filters will be added as demo data."),
|
'Icon',
|
||||||
'fields': fields.many2many('ir.model.fields', 'prototype_fields_rel',
|
help=('The icon set up here will be used as the icon '
|
||||||
'prototype_id', 'field_id', 'Fields'),
|
'for the exported module also')
|
||||||
'menu': fields.many2many('ir.ui.menu', 'prototype_menu_rel',
|
)
|
||||||
'prototype_id', 'menu_id', 'Menu Items'),
|
version = fields.Char('Version', size=3, default='0.1')
|
||||||
'views': fields.many2many('ir.ui.view', 'prototype_view_rel',
|
auto_install = fields.Boolean(
|
||||||
'prototype_id', 'view_id', 'Views'),
|
'Auto Install',
|
||||||
'groups': fields.many2many('res.groups', 'prototype_groups_rel',
|
default=False,
|
||||||
'prototype_id', 'group_id', 'Groups'),
|
help='Check if the module should be install by default.'
|
||||||
'rights': fields.many2many('ir.model.access', 'prototype_rights_rel',
|
)
|
||||||
'prototype_id', 'right_id',
|
application = fields.Boolean(
|
||||||
'Access Rights'),
|
'Application',
|
||||||
'rules': fields.many2many('ir.rule', 'prototype_rule_rel',
|
default=False,
|
||||||
'prototype_id', 'rule_id', 'Record Rules'),
|
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'
|
||||||
|
)
|
||||||
|
|
||||||
_defaults = {
|
__data_files = []
|
||||||
'auto_install': False,
|
__field_descriptions = {}
|
||||||
'version': '0.1',
|
_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))
|
||||||
|
|
|
@ -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,
|
|
||||||
}
|
|
|
@ -1,8 +0,0 @@
|
||||||
<?xml version="1.0"?>
|
|
||||||
<openerp>
|
|
||||||
<data>
|
|
||||||
|
|
||||||
$data
|
|
||||||
|
|
||||||
</data>
|
|
||||||
</openerp>
|
|
|
@ -1,8 +0,0 @@
|
||||||
<?xml version="1.0"?>
|
|
||||||
<openerp>
|
|
||||||
<data>
|
|
||||||
|
|
||||||
$demo
|
|
||||||
|
|
||||||
</data>
|
|
||||||
</openerp>
|
|
|
@ -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
|
|
||||||
)
|
|
|
@ -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 +0,0 @@
|
||||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
{% extends "header.template" %}
|
||||||
|
{% block body %}
|
||||||
|
{% if models -%}
|
||||||
|
from . import models
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
|
@ -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,
|
|
||||||
}
|
|
|
@ -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,7 +2,7 @@
|
||||||
<openerp>
|
<openerp>
|
||||||
<data>
|
<data>
|
||||||
|
|
||||||
$data
|
{{ data }}
|
||||||
|
|
||||||
</data>
|
</data>
|
||||||
</openerp>
|
</openerp>
|
|
@ -1,8 +0,0 @@
|
||||||
<?xml version="1.0"?>
|
|
||||||
<openerp>
|
|
||||||
<data>
|
|
||||||
|
|
||||||
$demo
|
|
||||||
|
|
||||||
</data>
|
|
||||||
</openerp>
|
|
|
@ -2,7 +2,7 @@
|
||||||
<openerp>
|
<openerp>
|
||||||
<data>
|
<data>
|
||||||
|
|
||||||
$views
|
{{ demo }}
|
||||||
|
|
||||||
</data>
|
</data>
|
||||||
</openerp>
|
</openerp>
|
|
@ -2,8 +2,8 @@
|
||||||
##############################################################################
|
##############################################################################
|
||||||
#
|
#
|
||||||
# Odoo, Open Source Management Solution
|
# Odoo, Open Source Management Solution
|
||||||
# This module copyright (C) $generation_date $author
|
# This module copyright (C) {{ export_year }} {{ author }}
|
||||||
# (<$website>).
|
# ({{ 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 %}
|
|
@ -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
|
|
||||||
)
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
{% extends "header.template" %}
|
||||||
|
{% block body %}
|
||||||
|
{% for model in models -%}
|
||||||
|
from . import {{ model }}
|
||||||
|
{% endfor %}
|
||||||
|
{% endblock %}
|
|
@ -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 = {
|
|
||||||
|
|
||||||
}
|
|
|
@ -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 +0,0 @@
|
||||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
|
|
|
@ -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 %}
|
|
@ -1,10 +0,0 @@
|
||||||
<?xml version="1.0"?>
|
|
||||||
<openerp>
|
|
||||||
<data>
|
|
||||||
|
|
||||||
$groups
|
|
||||||
|
|
||||||
$rules
|
|
||||||
|
|
||||||
</data>
|
|
||||||
</openerp>
|
|
|
@ -2,9 +2,9 @@
|
||||||
<openerp>
|
<openerp>
|
||||||
<data>
|
<data>
|
||||||
|
|
||||||
$groups
|
{{ groups }}
|
||||||
|
|
||||||
$rules
|
{{ rules }}
|
||||||
|
|
||||||
</data>
|
</data>
|
||||||
</openerp>
|
</openerp>
|
|
@ -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>
|
|
@ -1,8 +0,0 @@
|
||||||
<?xml version="1.0"?>
|
|
||||||
<openerp>
|
|
||||||
<data>
|
|
||||||
|
|
||||||
$views
|
|
||||||
|
|
||||||
</data>
|
|
||||||
</openerp>
|
|
|
@ -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>
|
|
@ -1,9 +1,9 @@
|
||||||
# -*- encoding: utf-8 -*-
|
# -*- encoding: utf-8 -*-
|
||||||
##############################################################################
|
##############################################################################
|
||||||
#
|
#
|
||||||
# Odoo, Open Source Management Solution
|
# OpenERP, Open Source Management Solution
|
||||||
# This module copyright (C) $generation_date $author
|
# This module copyright (C) 2010 - 2014 Savoir-faire Linux
|
||||||
# (<$website>).
|
# (<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,
|
||||||
|
]
|
|
@ -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('.', '_')
|
||||||
|
)
|
|
@ -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
|
||||||
|
)
|
|
@ -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">
|
<record id="view_prototype_tree" model="ir.ui.view">
|
||||||
<field name="name">prototype.tree</field>
|
<field name="name">prototype.tree</field>
|
||||||
<field name="model">prototype</field>
|
<field name="model">prototype</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<tree string="Prototype">
|
<tree string="Prototype">
|
||||||
<field name="name"/>
|
<field name="human_name"/>
|
||||||
<field name="shortdesc"/>
|
<field name="name"/>
|
||||||
<field name="summary"/>
|
<field name="summary"/>
|
||||||
</tree>
|
</tree>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="view_prototype_form" model="ir.ui.view">
|
<record id="view_prototype_form" model="ir.ui.view">
|
||||||
<field name="name">prototype.form</field>
|
<field name="name">prototype.form</field>
|
||||||
<field name="model">prototype</field>
|
<field name="model">prototype</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<form string="Module">
|
<form string="Module">
|
||||||
<link rel="stylesheet" href="/base/static/src/css/description.css"/>
|
<link rel="stylesheet"
|
||||||
<sheet>
|
href="/base/static/src/css/description.css"/>
|
||||||
<field name="icon_image" widget="image" class="oe_avatar oe_left"/>
|
<sheet>
|
||||||
<div class="oe_title">
|
<field name="icon_image" widget="image"
|
||||||
<h1><field name="summary"/></h1>
|
class="oe_avatar oe_left"/>
|
||||||
<div>
|
<div class="oe_title">
|
||||||
<button name="%(button_module_export_action)d" string="Export" type="action"/>
|
<h1>
|
||||||
</div>
|
<field name="human_name"/>
|
||||||
</div>
|
</h1>
|
||||||
<group>
|
<div>
|
||||||
<group>
|
<button name="%(button_module_export_action)d"
|
||||||
<field name="name"/>
|
string="Export" type="action"/>
|
||||||
<field name="shortdesc"/>
|
</div>
|
||||||
<field name="category_id"/>
|
</div>
|
||||||
<field name="version"/>
|
<group>
|
||||||
</group>
|
<group>
|
||||||
<group>
|
<field name="name"/>
|
||||||
<field name="author"/>
|
<field name="summary"/>
|
||||||
<field name="website" widget="url"/>
|
<field name="category_id"/>
|
||||||
<field name="maintainer"/>
|
<field name="version"/>
|
||||||
<field name="auto_install"/>
|
</group>
|
||||||
</group>
|
<group>
|
||||||
</group>
|
<field name="author"/>
|
||||||
<notebook>
|
<field name="website" widget="url"/>
|
||||||
<page string="Description">
|
<field name="maintainer"/>
|
||||||
<field name="description"/>
|
<field name="auto_install"/>
|
||||||
</page>
|
<field name="application"/>
|
||||||
<page string="Dependencies">
|
</group>
|
||||||
<field name="depends"/>
|
</group>
|
||||||
</page>
|
<notebook>
|
||||||
<page string="Data & Demo">
|
<page string="Description">
|
||||||
<label for="data"/>
|
<field name="description"/>
|
||||||
<field name="data"/>
|
</page>
|
||||||
<label for="demo"/>
|
<page string="Dependencies">
|
||||||
<field name="demo"/>
|
<field name="dependency_ids"/>
|
||||||
</page>
|
</page>
|
||||||
<page string="Models">
|
<page string="Data & Demo">
|
||||||
<label for="fields"/>
|
<label for="data_ids"/>
|
||||||
<field name="fields" domain="[('state', '=', 'manual')]"/>
|
<field name="data_ids"/>
|
||||||
</page>
|
<label for="demo_ids"/>
|
||||||
<page string="Interface">
|
<field name="demo_ids"/>
|
||||||
<label for="menu"/>
|
</page>
|
||||||
<field name="menu"/>
|
<page string="Fields">
|
||||||
<label for="views"/>
|
<label for="field_ids"/>
|
||||||
<field name="views"/>
|
<field name="field_ids"/>
|
||||||
</page>
|
</page>
|
||||||
<page string="Security">
|
<page string="Interface">
|
||||||
<label for="groups"/>
|
<label for="menu_ids"/>
|
||||||
<field name="groups"/>
|
<field name="menu_ids"/>
|
||||||
<label for="rights"/>
|
<label for="view_ids"/>
|
||||||
<field name="rights"/>
|
<field name="view_ids"/>
|
||||||
<label for="rules"/>
|
</page>
|
||||||
<field name="rules"/>
|
<page string="Security">
|
||||||
</page>
|
<label for="group_ids"/>
|
||||||
</notebook>
|
<field name="group_ids"/>
|
||||||
</sheet>
|
<label for="right_ids"/>
|
||||||
</form>
|
<field name="right_ids"/>
|
||||||
</field>
|
<label for="rule_ids"/>
|
||||||
</record>
|
<field name="rule_ids"/>
|
||||||
|
</page>
|
||||||
|
</notebook>
|
||||||
|
</sheet>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
<record id="open_prototype_list" model="ir.actions.act_window">
|
<record id="open_prototype_list" model="ir.actions.act_window">
|
||||||
<field name="name">Prototype</field>
|
<field name="name">Prototype</field>
|
||||||
<field name="res_model">prototype</field>
|
<field name="res_model">prototype</field>
|
||||||
<field name="view_type">form</field>
|
<field name="view_type">form</field>
|
||||||
<field name="view_mode">tree,form</field>
|
<field name="view_mode">tree,form</field>
|
||||||
<field name="view_id" ref="view_prototype_tree"/>
|
<field name="view_id" ref="view_prototype_tree"/>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<menuitem id="menu_open_prototype"
|
<menuitem id="menu_open_prototype"
|
||||||
action="open_prototype_list"
|
action="open_prototype_list"
|
||||||
parent="base.menu_management"
|
parent="base.menu_management"
|
||||||
sequence="30"
|
sequence="30"
|
||||||
groups="base.group_system"/>
|
groups="base.group_system"/>
|
||||||
|
|
||||||
</data>
|
</data>
|
||||||
</openerp>
|
</openerp>
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
# -*- 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
|
||||||
# (<http://www.savoirfairelinux.com>).
|
# (<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
|
||||||
# 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
|
import StringIO
|
||||||
from openerp.tools.translate import _
|
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)
|
||||||
'name': fields.char('File Name', readonly=True),
|
# It is implemented in order to manage previous and next versions
|
||||||
'api_version': fields.selection([('8.0','8.0'),
|
# of odoo
|
||||||
('7.0', '7.0')], 'API version',
|
api_version = fields.Selection(
|
||||||
required=True),
|
[
|
||||||
'data': fields.binary('File', readonly=True),
|
('8.0', '8.0'),
|
||||||
'state': fields.selection([('choose', 'choose'), # choose version
|
# ('7.0', '7.0')
|
||||||
('get', 'get')]) # get module
|
],
|
||||||
}
|
'API version',
|
||||||
_defaults = {
|
required=True,
|
||||||
'state': 'choose',
|
default='8.0'
|
||||||
'api_version': '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)
|
||||||
|
|
||||||
# TODO: Implement the export logic here
|
active_model = self._context.get('active_model')
|
||||||
filename = 'new'
|
|
||||||
this.name = "%s.%s" % (filename, 'zip')
|
# checking if the wizard was called by a prototype.
|
||||||
out = 'toto'
|
assert active_model == 'prototype', \
|
||||||
# /TODO
|
'{} 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)
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
|
|
@ -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…
Reference in New Issue