Browse Source

[REF] module_prototyper: Black python code

12.0-mig-module_prototyper_last
Nicolas JEUDY 5 years ago
parent
commit
b2a62a7b41
  1. 5
      module_prototyper/__init__.py
  2. 40
      module_prototyper/__manifest__.py
  3. 6
      module_prototyper/models/__init__.py
  4. 13
      module_prototyper/models/ir_model_fields.py
  5. 376
      module_prototyper/models/module_prototyper.py
  6. 2
      module_prototyper/models/module_prototyper_api_version.py
  7. 5
      module_prototyper/tests/__init__.py
  8. 64
      module_prototyper/tests/test_prototype.py
  9. 52
      module_prototyper/tests/test_prototype_module_export.py
  10. 53
      module_prototyper/wizard/module_prototyper_module_export.py

5
module_prototyper/__init__.py

@ -19,7 +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,
wizard
)
from . import models, wizard

40
module_prototyper/__manifest__.py

@ -21,26 +21,24 @@
############################################################################## ##############################################################################
{ {
'name': 'Module Prototyper',
'version': '10.0.1.0.0',
'author': 'Savoir-faire Linux, Sudokeys, Onestein, '
'Odoo Community Association (OCA)',
'maintainer': 'Savoir-faire Linux',
'website': 'http://www.savoirfairelinux.com',
'license': 'AGPL-3',
'category': 'Others',
'summary': 'Prototype your module.',
'depends': [],
'external_dependencies': {
'python': [],
},
'data': [
'data/module_prototyper_api_version_data.xml',
'wizard/module_prototyper_module_export_view.xml',
'views/module_prototyper_view.xml',
'views/ir_model_fields_view.xml',
'security/ir.model.access.csv',
"name": "Module Prototyper",
"version": "10.0.1.0.0",
"author": "Savoir-faire Linux, Sudokeys, Onestein, "
"Odoo Community Association (OCA)",
"maintainer": "Savoir-faire Linux",
"website": "http://www.savoirfairelinux.com",
"license": "AGPL-3",
"category": "Others",
"summary": "Prototype your module.",
"depends": [],
"external_dependencies": {"python": []},
"data": [
"data/module_prototyper_api_version_data.xml",
"wizard/module_prototyper_module_export_view.xml",
"views/module_prototyper_view.xml",
"views/ir_model_fields_view.xml",
"security/ir.model.access.csv",
], ],
'installable': True,
'application': True,
"installable": True,
"application": True,
} }

6
module_prototyper/models/__init__.py

@ -19,8 +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 (
module_prototyper,
module_prototyper_api_version,
ir_model_fields
)
from . import module_prototyper, module_prototyper_api_version, ir_model_fields

13
module_prototyper/models/ir_model_fields.py

@ -25,24 +25,25 @@ from odoo import fields, models
class IrModelFields(models.Model): class IrModelFields(models.Model):
"""Addition of text fields to fields.""" """Addition of text fields to fields."""
_inherit = "ir.model.fields" _inherit = "ir.model.fields"
notes = fields.Text('Notes to developers.')
helper = fields.Text('Helper')
notes = fields.Text("Notes to developers.")
helper = fields.Text("Helper")
# TODO: Make column1 and 2 required if a model has a m2m to itself # TODO: Make column1 and 2 required if a model has a m2m to itself
column1 = fields.Char( column1 = fields.Char(
'Column1',
"Column1",
help="name of the column referring to 'these' records in the " help="name of the column referring to 'these' records in the "
"relation table", "relation table",
) )
column2 = fields.Char( column2 = fields.Char(
'Column2',
"Column2",
help="name of the column referring to 'those' records in the " help="name of the column referring to 'those' records in the "
"relation table", "relation table",
) )
limit = fields.Integer('Read limit', help="Read limit")
limit = fields.Integer("Read limit", help="Read limit")
client_context = fields.Char( client_context = fields.Char(
'Context',
"Context",
help="Context to use on the client side when handling the field " help="Context to use on the client side when handling the field "
"(python dictionary)", "(python dictionary)",
) )

376
module_prototyper/models/module_prototyper.py

@ -48,6 +48,7 @@ class ModulePrototyper(models.Model):
by a developer to fix glitch that would sneak it during the generation of by a developer to fix glitch that would sneak it during the generation of
files but also to add not supported features. files but also to add not supported features.
""" """
_name = "module_prototyper" _name = "module_prototyper"
_description = "Module Prototyper" _description = "Module Prototyper"
@ -55,151 +56,214 @@ class ModulePrototyper(models.Model):
""" """
Extract the content of default description Extract the content of default description
""" """
filepath = '%s/../data/README.rst' % (os.path.dirname(__file__),)
with open(filepath, 'r') as content_file:
filepath = "%s/../data/README.rst" % (os.path.dirname(__file__),)
with open(filepath, "r") as content_file:
content = content_file.read() content = content_file.read()
return content return content
license = fields.Selection( license = fields.Selection(
[ [
(licenses.GPL3, 'GPL Version 3'),
(licenses.GPL3_L, 'GPL-3 or later version'),
(licenses.LGPL3, 'LGPL-3'),
(licenses.LGPL3_L, 'LGPL-3 or later version'),
(licenses.AGPL3, 'Affero GPL-3'),
(licenses.OSI, 'Other OSI Approved Licence'),
('Other proprietary', 'Other Proprietary')
(licenses.GPL3, "GPL Version 3"),
(licenses.GPL3_L, "GPL-3 or later version"),
(licenses.LGPL3, "LGPL-3"),
(licenses.LGPL3_L, "LGPL-3 or later version"),
(licenses.AGPL3, "Affero GPL-3"),
(licenses.OSI, "Other OSI Approved Licence"),
("Other proprietary", "Other Proprietary"),
], ],
string='License',
string="License",
default=licenses.AGPL3, default=licenses.AGPL3,
) )
name = fields.Char( 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.')
"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')
category_id = fields.Many2one("ir.module.category", "Category")
human_name = fields.Char( human_name = fields.Char(
'Module Name', required=True,
help=('The Module Name will be used as the displayed name of the '
'exported module.')
"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, help=("Enter a summary of your module")
) )
summary = fields.Char('Summary', required=True,
help=('Enter a summary of your module'))
description = fields.Text( description = fields.Text(
'Description',
"Description",
required=True, required=True,
help=('Enter the description of your module, what it does, how to '
'install, configure and use it, the roadmap or known issues. '
'The description will be exported in README.rst'),
default=get_default_description
)
author = fields.Char('Author', required=True, help=('Enter your name'))
help=(
"Enter the description of your module, what it does, how to "
"install, configure and use it, the roadmap or known issues. "
"The description will be exported in README.rst"
),
default=get_default_description,
)
author = fields.Char("Author", required=True, help=("Enter your name"))
maintainer = fields.Char( maintainer = fields.Char(
'Maintainer',
help=('Enter the name of the person or organization who will '
'maintain this module')
"Maintainer",
help=(
"Enter the name of the person or organization who will "
"maintain this module"
),
) )
website = fields.Char('Website', help=('Enter the URL of your website'))
website = fields.Char("Website", help=("Enter the URL of your website"))
icon_image = fields.Binary( icon_image = fields.Binary(
'Icon',
help=('The icon set up here will be used as the icon '
'for the exported module also')
"Icon",
help=(
"The icon set up here will be used as the icon "
"for the exported module also"
),
) )
version = fields.Char( version = fields.Char(
'Version',
"Version",
size=10, size=10,
default='10.0.1.0.0',
help=('Enter the version of your module with 5 digits')
default="10.0.1.0.0",
help=("Enter the version of your module with 5 digits"),
) )
auto_install = fields.Boolean( auto_install = fields.Boolean(
'Auto Install',
"Auto Install",
default=False, default=False,
help='Check if the module should be install by default.'
help="Check if the module should be install by default.",
) )
application = fields.Boolean( application = fields.Boolean(
'Application',
"Application",
default=False, default=False,
help='Check if the module is an Odoo application.'
help="Check if the module is an Odoo application.",
) )
# Relations # Relations
dependency_ids = fields.Many2many( dependency_ids = fields.Many2many(
'ir.module.module', 'module_prototyper_module_rel',
'module_prototyper_id', 'module_id',
'Dependencies',
help=('Enter the list of required modules that need to be installed '
'for your module to work properly')
"ir.module.module",
"module_prototyper_module_rel",
"module_prototyper_id",
"module_id",
"Dependencies",
help=(
"Enter the list of required modules that need to be installed "
"for your module to work properly"
),
) )
data_ids = fields.Many2many( data_ids = fields.Many2many(
'ir.filters',
'prototype_data_rel',
'module_prototyper_id', 'filter_id',
'Data filters',
help="The records matching the filters will be added as data."
"ir.filters",
"prototype_data_rel",
"module_prototyper_id",
"filter_id",
"Data filters",
help="The records matching the filters will be added as data.",
) )
demo_ids = fields.Many2many( demo_ids = fields.Many2many(
'ir.filters',
'prototype_demo_rel',
'module_prototyper_id', 'filter_id',
'Demo filters',
help="The records matching the filters will be added as demo data."
"ir.filters",
"prototype_demo_rel",
"module_prototyper_id",
"filter_id",
"Demo filters",
help="The records matching the filters will be added as demo data.",
) )
field_ids = fields.Many2many( field_ids = fields.Many2many(
'ir.model.fields', 'prototype_fields_rel',
'module_prototyper_id', 'field_id', 'Fields',
help=('Enter the list of fields that you have created or modified '
'and want to export in this module. New models will be '
'exported as long as you choose one of his fields.')
"ir.model.fields",
"prototype_fields_rel",
"module_prototyper_id",
"field_id",
"Fields",
help=(
"Enter the list of fields that you have created or modified "
"and want to export in this module. New models will be "
"exported as long as you choose one of his fields."
),
) )
menu_ids = fields.Many2many( menu_ids = fields.Many2many(
'ir.ui.menu', 'prototype_menu_rel',
'module_prototyper_id', 'menu_id', 'Menu Items',
help=('Enter the list of menu items that you have created and want '
'to export in this module. Related windows actions will be '
'exported as well.')
"ir.ui.menu",
"prototype_menu_rel",
"module_prototyper_id",
"menu_id",
"Menu Items",
help=(
"Enter the list of menu items that you have created and want "
"to export in this module. Related windows actions will be "
"exported as well."
),
) )
view_ids = fields.Many2many( view_ids = fields.Many2many(
'ir.ui.view', 'prototype_view_rel',
'module_prototyper_id', 'view_id', 'Views',
help=('Enter the list of views that you have created and want to '
'export in this module.')
"ir.ui.view",
"prototype_view_rel",
"module_prototyper_id",
"view_id",
"Views",
help=(
"Enter the list of views that you have created and want to "
"export in this module."
),
) )
group_ids = fields.Many2many( group_ids = fields.Many2many(
'res.groups', 'prototype_groups_rel',
'module_prototyper_id', 'group_id', 'Groups',
help=('Enter the list of groups that you have created and want to '
'export in this module.')
"res.groups",
"prototype_groups_rel",
"module_prototyper_id",
"group_id",
"Groups",
help=(
"Enter the list of groups that you have created and want to "
"export in this module."
),
) )
right_ids = fields.Many2many( right_ids = fields.Many2many(
'ir.model.access', 'prototype_rights_rel',
'module_prototyper_id', 'right_id',
'Access Rights',
help=('Enter the list of access rights that you have created and '
'want to export in this module.')
"ir.model.access",
"prototype_rights_rel",
"module_prototyper_id",
"right_id",
"Access Rights",
help=(
"Enter the list of access rights that you have created and "
"want to export in this module."
),
) )
rule_ids = fields.Many2many( rule_ids = fields.Many2many(
'ir.rule', 'prototype_rule_rel',
'module_prototyper_id', 'rule_id', 'Record Rules',
help=('Enter the list of record rules that you have created and '
'want to export in this module.')
"ir.rule",
"prototype_rule_rel",
"module_prototyper_id",
"rule_id",
"Record Rules",
help=(
"Enter the list of record rules that you have created and "
"want to export in this module."
),
) )
report_ids = fields.Many2many( report_ids = fields.Many2many(
'ir.actions.report.xml', 'prototype_report_rel',
'module_prototyper_id', 'report_id', 'Reports',
help=('Enter the list of reports that you have created and '
'want to export in this module.')
"ir.actions.report.xml",
"prototype_report_rel",
"module_prototyper_id",
"report_id",
"Reports",
help=(
"Enter the list of reports that you have created and "
"want to export in this module."
),
) )
activity_ids = fields.Many2many( activity_ids = fields.Many2many(
'workflow.activity', 'prototype_wf_activity_rel',
'module_prototyper_id', 'activity_id', 'Activities',
help=('Enter the list of workflow activities that you have created '
'and want to export in this module')
"workflow.activity",
"prototype_wf_activity_rel",
"module_prototyper_id",
"activity_id",
"Activities",
help=(
"Enter the list of workflow activities that you have created "
"and want to export in this module"
),
) )
transition_ids = fields.Many2many( transition_ids = fields.Many2many(
'workflow.transition', 'prototype_wf_transition_rel',
'module_prototyper_id', 'transition_id', 'Transitions',
help=('Enter the list of workflow transitions that you have created '
'and want to export in this module')
"workflow.transition",
"prototype_wf_transition_rel",
"module_prototyper_id",
"transition_id",
"Transitions",
help=(
"Enter the list of workflow transitions that you have created "
"and want to export in this module"
),
) )
_env = None _env = None
@ -207,8 +271,8 @@ class ModulePrototyper(models.Model):
_data_files = () _data_files = ()
_demo_files = () _demo_files = ()
_field_descriptions = None _field_descriptions = None
File_details = namedtuple('file_details', ['filename', 'filecontent'])
template_path = '%s/../templates/' % (os.path.dirname(__file__),)
File_details = namedtuple("file_details", ["filename", "filecontent"])
template_path = "%s/../templates/" % (os.path.dirname(__file__),)
@api.model @api.model
def setup_env(self, api_version): def setup_env(self, api_version):
@ -223,7 +287,7 @@ class ModulePrototyper(models.Model):
trim_blocks=True, trim_blocks=True,
loader=FileSystemLoader( loader=FileSystemLoader(
os.path.join(self.template_path, api_version.name) os.path.join(self.template_path, api_version.name)
)
),
) )
self._api_version = api_version self._api_version = api_version
return self._env return self._env
@ -240,12 +304,14 @@ class ModulePrototyper(models.Model):
# the mock will allow us to add data or modify the data # the mock will allow us to add data or modify the data
# of the field (like for the name) with keeping all the # of the field (like for the name) with keeping all the
# attributes of the record. # attributes of the record.
field_description.update({
field_description.update(
{
attr_name: getattr(field, attr_name) attr_name: getattr(field, attr_name)
for attr_name in dir(field) for attr_name in dir(field)
if not attr_name[0] == '_'
})
field_description['name'] = self.unprefix(field.name)
if not attr_name[0] == "_"
}
)
field_description["name"] = self.unprefix(field.name)
self._field_descriptions[field] = field_description self._field_descriptions[field] = field_description
@api.model @api.model
@ -253,8 +319,9 @@ class ModulePrototyper(models.Model):
""" Generates the files from the details of the prototype. """ Generates the files from the details of the prototype.
:return: tuple :return: tuple
""" """
assert self._env is not None, \
'Run set_env(api_version) before to generate files.'
assert (
self._env is not None
), "Run set_env(api_version) before to generate files."
# Avoid sharing these across instances # Avoid sharing these across instances
self._data_files = [] self._data_files = []
@ -269,9 +336,7 @@ class ModulePrototyper(models.Model):
file_details.extend(self.generate_data_files()) file_details.extend(self.generate_data_files())
# must be the last as the other generations might add information # must be the last as the other generations might add information
# to put in the __openerp__: additional dependencies, views files, etc. # to put in the __openerp__: additional dependencies, views files, etc.
file_details.append(
self.generate_module_openerp_file_details()
)
file_details.append(self.generate_module_openerp_file_details())
if self.icon_image: if self.icon_image:
file_details.append(self.save_icon()) file_details.append(self.save_icon())
@ -290,17 +355,17 @@ class ModulePrototyper(models.Model):
# * add document as a dependency. # * add document as a dependency.
# The second options seems to be better, as Document is a base module. # The second options seems to be better, as Document is a base module.
return self.File_details( return self.File_details(
os.path.join('static', 'description', 'icon.jpg'),
base64.b64decode(self.icon_image)
os.path.join("static", "description", "icon.jpg"),
base64.b64decode(self.icon_image),
) )
@api.model @api.model
def generate_module_openerp_file_details(self): def generate_module_openerp_file_details(self):
"""Wrapper to generate the __openerp__.py file of the module.""" """Wrapper to generate the __openerp__.py file of the module."""
fn_inc_ext = '%s.py' % (self._api_version.manifest_file_name,)
fn_inc_ext = "%s.py" % (self._api_version.manifest_file_name,)
return self.generate_file_details( return self.generate_file_details(
fn_inc_ext, fn_inc_ext,
'%s.template' % (fn_inc_ext,),
"%s.template" % (fn_inc_ext,),
prototype=self, prototype=self,
data_files=self._data_files, data_files=self._data_files,
demo_fiels=self._demo_files, demo_fiels=self._demo_files,
@ -310,11 +375,11 @@ class ModulePrototyper(models.Model):
def generate_module_init_file_details(self): def generate_module_init_file_details(self):
"""Wrapper to generate the __init__.py file of the module.""" """Wrapper to generate the __init__.py file of the module."""
return self.generate_file_details( return self.generate_file_details(
'__init__.py',
'__init__.py.template',
"__init__.py",
"__init__.py.template",
# no import models if no work of fields in # no import models if no work of fields in
# the prototype # the prototype
models=bool(self.field_ids)
models=bool(self.field_ids),
) )
@api.model @api.model
@ -335,7 +400,7 @@ class ModulePrototyper(models.Model):
relations = {} relations = {}
field_descriptions = self._field_descriptions or {} field_descriptions = self._field_descriptions or {}
for field in field_descriptions.itervalues(): for field in field_descriptions.itervalues():
model = field.get('model_id')
model = field.get("model_id")
relations.setdefault(model, []).append(field) relations.setdefault(model, []).append(field)
# dependencies.add(model.id) # dependencies.add(model.id)
@ -354,12 +419,11 @@ class ModulePrototyper(models.Model):
def generate_models_init_details(self, ir_models): def generate_models_init_details(self, ir_models):
"""Wrapper to generate the __init__.py file in models folder.""" """Wrapper to generate the __init__.py file in models folder."""
return self.generate_file_details( return self.generate_file_details(
'models/__init__.py',
'models/__init__.py.template',
"models/__init__.py",
"models/__init__.py.template",
models=[ models=[
self.friendly_name(ir_model.model)
for ir_model in ir_models
]
self.friendly_name(ir_model.model) for ir_model in ir_models
],
) )
@api.model @api.model
@ -371,14 +435,12 @@ class ModulePrototyper(models.Model):
views_details = [] views_details = []
for model, views in relations.iteritems(): for model, views in relations.iteritems():
filepath = 'views/%s_view.xml' % (
filepath = "views/%s_view.xml" % (
self.friendly_name(self.unprefix(model)), self.friendly_name(self.unprefix(model)),
) )
views_details.append( views_details.append(
self.generate_file_details( self.generate_file_details(
filepath,
'views/model_views.xml.template',
views=views
filepath, "views/model_views.xml.template", views=views
) )
) )
self._data_files.append(filepath) self._data_files.append(filepath)
@ -393,20 +455,16 @@ class ModulePrototyper(models.Model):
if menu.action and menu.action.res_model: if menu.action and menu.action.res_model:
model = self.unprefix(menu.action.res_model) model = self.unprefix(menu.action.res_model)
else: else:
model = 'ir_ui'
model = "ir_ui"
relations.setdefault(model, []).append(menu) relations.setdefault(model, []).append(menu)
menus_details = [] menus_details = []
for model_name, menus in relations.iteritems(): for model_name, menus in relations.iteritems():
model_name = self.unprefix(model_name) model_name = self.unprefix(model_name)
filepath = 'views/%s_menus.xml' % (
self.friendly_name(model_name),
)
filepath = "views/%s_menus.xml" % (self.friendly_name(model_name),)
menus_details.append( menus_details.append(
self.generate_file_details( self.generate_file_details(
filepath,
'views/model_menus.xml.template',
menus=menus,
filepath, "views/model_menus.xml.template", menus=menus
) )
) )
self._data_files.append(filepath) self._data_files.append(filepath)
@ -423,8 +481,8 @@ class ModulePrototyper(models.Model):
""" """
python_friendly_name = self.friendly_name(self.unprefix(model.model)) python_friendly_name = self.friendly_name(self.unprefix(model.model))
return self.generate_file_details( return self.generate_file_details(
'models/%s.py' % (python_friendly_name,),
'models/model_name.py.template',
"models/%s.py" % (python_friendly_name,),
"models/model_name.py.template",
name=python_friendly_name, name=python_friendly_name,
model=model, model=model,
fields=field_descriptions, fields=field_descriptions,
@ -434,12 +492,8 @@ class ModulePrototyper(models.Model):
def generate_data_files(self): def generate_data_files(self):
""" Generate data and demo files """ """ Generate data and demo files """
data, demo = {}, {} data, demo = {}, {}
filters = [
(data, ir_filter)
for ir_filter in self.data_ids
] + [
(demo, ir_filter)
for ir_filter in self.demo_ids
filters = [(data, ir_filter) for ir_filter in self.data_ids] + [
(demo, ir_filter) for ir_filter in self.demo_ids
] ]
for target, ir_filter in filters: for target, ir_filter in filters:
@ -450,19 +504,22 @@ class ModulePrototyper(models.Model):
res = [] res = []
for prefix, model_data, file_list in [ for prefix, model_data, file_list in [
('data', data, self._data_files),
('demo', demo, self._demo_files)]:
("data", data, self._data_files),
("demo", demo, self._demo_files),
]:
for model_name, records in model_data.iteritems(): for model_name, records in model_data.iteritems():
fname = self.friendly_name(self.unprefix(model_name)) fname = self.friendly_name(self.unprefix(model_name))
filename = '%s/%s.xml' % (prefix, fname)
filename = "%s/%s.xml" % (prefix, fname)
self._data_files.append(filename) self._data_files.append(filename)
res.append(self.generate_file_details(
res.append(
self.generate_file_details(
filename, filename,
'data/model_name.xml.template',
"data/model_name.xml.template",
model=model_name, model=model_name,
records=records, records=records,
))
)
)
return res return res
@ -470,15 +527,15 @@ class ModulePrototyper(models.Model):
def unprefix(cls, name): def unprefix(cls, name):
if not name: if not name:
return name return name
return re.sub('^x_', '', name)
return re.sub("^x_", "", name)
@classmethod @classmethod
def is_prefixed(cls, name): def is_prefixed(cls, name):
return bool(re.match('^x_', name))
return bool(re.match("^x_", name))
@classmethod @classmethod
def friendly_name(cls, name): def friendly_name(cls, name):
return name.replace('.', '_')
return name.replace(".", "_")
@classmethod @classmethod
def fixup_domain(cls, domain): def fixup_domain(cls, domain):
@ -501,8 +558,10 @@ class ModulePrototyper(models.Model):
try: try:
attrs = safe_eval(elem.attrib["attrs"]) attrs = safe_eval(elem.attrib["attrs"])
except Exception: except Exception:
_logger.error("Unable to eval attribute: %s, skipping",
elem.attrib["attrs"])
_logger.error(
"Unable to eval attribute: %s, skipping",
elem.attrib["attrs"],
)
continue continue
if isinstance(attrs, dict): if isinstance(attrs, dict):
@ -530,17 +589,16 @@ class ModulePrototyper(models.Model):
# keywords used in several templates. # keywords used in several templates.
kwargs.update( kwargs.update(
{ {
'export_year': date.today().year,
'author': self.author,
'website': self.website,
'license_text': licenses.get_license_text(self.license),
'cr': self._cr,
"export_year": date.today().year,
"author": self.author,
"website": self.website,
"license_text": licenses.get_license_text(self.license),
"cr": self._cr,
# Utility functions # Utility functions
'fixup_arch': self.fixup_arch,
'is_prefixed': self.is_prefixed,
'unprefix': self.unprefix,
'wrap': wrap,
"fixup_arch": self.fixup_arch,
"is_prefixed": self.is_prefixed,
"unprefix": self.unprefix,
"wrap": wrap,
} }
) )
return self.File_details(filename, template.render(kwargs)) return self.File_details(filename, template.render(kwargs))

2
module_prototyper/models/module_prototyper_api_version.py

@ -6,7 +6,7 @@ from odoo import models, fields
class ModulePrototyperApiVersion(models.Model): class ModulePrototyperApiVersion(models.Model):
_name = 'module_prototyper.api_version'
_name = "module_prototyper.api_version"
name = fields.Char() name = fields.Char()
manifest_file_name = fields.Char() manifest_file_name = fields.Char()

5
module_prototyper/tests/__init__.py

@ -19,7 +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 (
test_prototype_module_export,
test_prototype
)
from . import test_prototype_module_export, test_prototype

64
module_prototyper/tests/test_prototype.py

@ -34,24 +34,26 @@ from odoo.tests import common
class TestModulePrototyper(common.TransactionCase): class TestModulePrototyper(common.TransactionCase):
def setUp(self): def setUp(self):
super(TestModulePrototyper, self).setUp() super(TestModulePrototyper, self).setUp()
self.main_model = self.env['module_prototyper']
self.module_category_model = self.env['ir.module.category']
self.module_module_model = self.env['ir.module.module']
self.main_model = self.env["module_prototyper"]
self.module_category_model = self.env["ir.module.category"]
self.module_module_model = self.env["ir.module.module"]
self.prototype = self.main_model.create({
'name': 't_name',
'category_id': self.module_category_model.browse(1).id,
'human_name': 't_human_name',
'summary': 't_summary',
'description': 't_description',
'author': 't_author',
'maintainer': 't_maintainer',
'website': 't_website',
'dependencies': [(6, 0, [1, 2, 3, 4])],
})
self.api_version = self.env['module_prototyper.api_version'].search([
('id', '=', self.ref('module_prototyper.api_version_80'))
])
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 = self.env["module_prototyper.api_version"].search(
[("id", "=", self.ref("module_prototyper.api_version_80"))]
)
def test_generate_files_assert_if_no_env(self): def test_generate_files_assert_if_no_env(self):
with self.assertRaises(AssertionError): with self.assertRaises(AssertionError):
@ -74,15 +76,13 @@ class TestModulePrototyper(common.TransactionCase):
contents = contents.encode("utf-8") contents = contents.encode("utf-8")
ast.parse(contents) ast.parse(contents)
if pep8: if pep8:
checker = pep8.Checker(
name,
contents.splitlines(True))
checker = pep8.Checker(name, contents.splitlines(True))
res = checker.check_all() res = checker.check_all()
self.assertFalse( self.assertFalse(
res, res,
"Python file %s has pep8 errors:\n" "Python file %s has pep8 errors:\n"
"%s\n%s" % (name, checker.report.messages,
repr(contents))
"%s\n%s"
% (name, checker.report.messages, repr(contents)),
) )
elif name.endswith(".xml"): elif name.endswith(".xml"):
@ -90,9 +90,9 @@ class TestModulePrototyper(common.TransactionCase):
lxml.etree.fromstring(contents) lxml.etree.fromstring(contents)
def test_generate_files_raise_templatenotfound_if_not_found(self): def test_generate_files_raise_templatenotfound_if_not_found(self):
not_existing_api = self.env['module_prototyper.api_version'].create({
'name': 'non_existing_api'
})
not_existing_api = self.env["module_prototyper.api_version"].create(
{"name": "non_existing_api"}
)
self.prototype.setup_env(not_existing_api) self.prototype.setup_env(not_existing_api)
with self.assertRaises(TemplateNotFound): with self.assertRaises(TemplateNotFound):
self.prototype.generate_files() self.prototype.generate_files()
@ -101,18 +101,12 @@ class TestModulePrototyper(common.TransactionCase):
"""test the jinja2 environment is set.""" """test the jinja2 environment is set."""
self.assertIsNone(self.prototype._env) self.assertIsNone(self.prototype._env)
self.prototype.setup_env(self.api_version) self.prototype.setup_env(self.api_version)
self.assertIsInstance(
self.prototype._env, Environment
)
self.assertEqual(
self.api_version,
self.prototype._api_version
)
self.assertIsInstance(self.prototype._env, Environment)
self.assertEqual(self.api_version, self.prototype._api_version)
def test_friendly_name_return(self): def test_friendly_name_return(self):
"""Test if the returns match the pattern.""" """Test if the returns match the pattern."""
name = 'res.partner'
name = "res.partner"
self.assertEqual( self.assertEqual(
self.prototype.friendly_name(name),
name.replace('.', '_')
self.prototype.friendly_name(name), name.replace(".", "_")
) )

52
module_prototyper/tests/test_prototype_module_export.py

@ -27,54 +27,48 @@ import StringIO
class TestPrototypeModuleExport(common.TransactionCase): class TestPrototypeModuleExport(common.TransactionCase):
def setUp(self): def setUp(self):
super(TestPrototypeModuleExport, self).setUp() super(TestPrototypeModuleExport, self).setUp()
self.main_model = self.env['module_prototyper.module.export']
self.prototype_model = self.env['module_prototyper']
self.module_category_model = self.env[
'ir.module.category'
]
self.main_model = self.env["module_prototyper.module.export"]
self.prototype_model = self.env["module_prototyper"]
self.module_category_model = self.env["ir.module.category"]
self.prototype = self.prototype_model.create({
'name': 't_name',
'category_id': self.module_category_model.browse(1).id,
'human_name': 't_human_name',
'summary': 't_summary',
'description': 't_description',
'author': 't_author',
'maintainer': 't_maintainer',
'website': 't_website',
})
self.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'})
self.exporter = self.main_model.create({"name": "t_name"})
def test_action_export_assert_for_wrong_active_model(self): def test_action_export_assert_for_wrong_active_model(self):
"""Test if the assertion raises.""" """Test if the assertion raises."""
exporter = self.main_model.with_context( exporter = self.main_model.with_context(
active_model='t_active_model'
active_model="t_active_model"
).create({}) ).create({})
self.assertRaises( self.assertRaises(
AssertionError,
exporter.action_export,
[exporter.id],
AssertionError, exporter.action_export, [exporter.id]
) )
def test_action_export_update_wizard(self): def test_action_export_update_wizard(self):
"""Test if the wizard is updated during the process.""" """Test if the wizard is updated during the process."""
exporter = self.main_model.with_context( exporter = self.main_model.with_context(
active_model=self.prototype_model._name, active_model=self.prototype_model._name,
active_id=self.prototype.id
active_id=self.prototype.id,
).create({}) ).create({})
exporter.action_export(exporter.id) exporter.action_export(exporter.id)
self.assertEqual(exporter.state, 'get')
self.assertEqual(exporter.name, '%s.zip' % (self.prototype.name,))
self.assertEqual(exporter.state, "get")
self.assertEqual(exporter.name, "%s.zip" % (self.prototype.name,))
def test_zip_files_returns_tuple(self): def test_zip_files_returns_tuple(self):
"""Test the method return of the method that generate the zip file.""" """Test the method return of the method that generate the zip file."""
ret = self.main_model.zip_files(self.exporter, [self.prototype]) ret = self.main_model.zip_files(self.exporter, [self.prototype])
self.assertIsInstance(ret, tuple) self.assertIsInstance(ret, tuple)
self.assertIsInstance(
ret.zip_file, zipfile.ZipFile
)
self.assertIsInstance(ret.zip_file, zipfile.ZipFile)
self.assertIsInstance(
ret.stringIO, StringIO.StringIO
)
self.assertIsInstance(ret.stringIO, StringIO.StringIO)

53
module_prototyper/wizard/module_prototyper_module_export.py

@ -33,22 +33,19 @@ class PrototypeModuleExport(models.TransientModel):
_name = "module_prototyper.module.export" _name = "module_prototyper.module.export"
def _default_api_version(self): def _default_api_version(self):
return self.env.ref('module_prototyper.api_version_100').id
return self.env.ref("module_prototyper.api_version_100").id
name = fields.Char('File Name', readonly=True)
name = fields.Char("File Name", readonly=True)
api_version = fields.Many2one( api_version = fields.Many2one(
comodel_name='module_prototyper.api_version',
string='API version',
comodel_name="module_prototyper.api_version",
string="API version",
required=True, required=True,
default=_default_api_version
default=_default_api_version,
) )
data = fields.Binary('File', readonly=True)
data = fields.Binary("File", readonly=True)
state = fields.Selection( state = fields.Selection(
[
('choose', 'choose'), # choose version
('get', 'get') # get module
],
default='choose'
[("choose", "choose"), ("get", "get")], # choose version # get module
default="choose",
) )
@api.model @api.model
@ -61,17 +58,15 @@ class PrototypeModuleExport(models.TransientModel):
ids = [ids] ids = [ids]
wizard = self.browse(ids) wizard = self.browse(ids)
active_model = self._context.get('active_model')
active_model = self._context.get("active_model")
# checking if the wizard was called by a prototype. # checking if the wizard was called by a prototype.
msg = '%s has to be called from a "module_prototyper" , not a "%s"' msg = '%s has to be called from a "module_prototyper" , not a "%s"'
assert active_model == 'module_prototyper', msg % (
self, active_model
)
assert active_model == "module_prototyper", msg % (self, active_model)
# getting the prototype of the wizard # getting the prototype of the wizard
prototypes = self.env[active_model].browse( prototypes = self.env[active_model].browse(
[self._context.get('active_id')]
[self._context.get("active_id")]
) )
zip_details = self.zip_files(wizard, prototypes) zip_details = self.zip_files(wizard, prototypes)
@ -83,20 +78,20 @@ class PrototypeModuleExport(models.TransientModel):
wizard.write( wizard.write(
{ {
'name': '%s.zip' % (zip_name,),
'state': 'get',
'data': base64.encodestring(zip_details.stringIO.getvalue())
"name": "%s.zip" % (zip_name,),
"state": "get",
"data": base64.encodestring(zip_details.stringIO.getvalue()),
} }
) )
return { return {
'type': 'ir.actions.act_window',
'res_model': 'module_prototyper.module.export',
'view_mode': 'form',
'view_type': 'form',
'res_id': wizard.id,
'views': [(False, 'form')],
'target': 'new',
"type": "ir.actions.act_window",
"res_model": "module_prototyper.module.export",
"view_mode": "form",
"view_type": "form",
"res_id": wizard.id,
"views": [(False, "form")],
"target": "new",
} }
@staticmethod @staticmethod
@ -105,10 +100,10 @@ class PrototypeModuleExport(models.TransientModel):
:param file_details: tuple (filename, file_content) :param file_details: tuple (filename, file_content)
:return: tuple (zip_file, stringIO) :return: tuple (zip_file, stringIO)
""" """
zip_details = namedtuple('Zip_details', ['zip_file', 'stringIO'])
zip_details = namedtuple("Zip_details", ["zip_file", "stringIO"])
out = StringIO.StringIO() out = StringIO.StringIO()
with zipfile.ZipFile(out, 'w') as target:
with zipfile.ZipFile(out, "w") as target:
for prototype in prototypes: for prototype in prototypes:
# setting the jinja environment. # setting the jinja environment.
# They will help the program to find the template to render the # They will help the program to find the template to render the
@ -122,7 +117,7 @@ class PrototypeModuleExport(models.TransientModel):
file_details = prototype.generate_files() file_details = prototype.generate_files()
for filename, file_content in file_details: for filename, file_content in file_details:
if isinstance(file_content, unicode): if isinstance(file_content, unicode):
file_content = file_content.encode('utf-8')
file_content = file_content.encode("utf-8")
# Prefix all names with module technical name # Prefix all names with module technical name
filename = os.path.join(prototype.name, filename) filename = os.path.join(prototype.name, filename)
info = zipfile.ZipInfo(filename) info = zipfile.ZipInfo(filename)

Loading…
Cancel
Save