Browse Source

empty selection in m2m field template

[FIX] other licenses, return lines as well

[FIX] license not shown in __oe__

[FIX] unprefix more names, try to get _name/_inherit right [IMP] group by module in zip

[FIX] fix category and summary being on same line

[FIX] fix export test

[IMP] add tabs for reports/security/workflow/data + partial data/demo generation

unprefix model names in __init__

[FIX] fix data file names in __openerp__.py

[IMP] move Data&Demo after Interface in view

[FIX] unprefix view file names

[IMP] remove prefixes from field attrs in views

[FIX] encode files in zip to utf-8, remove trailing comma in menu groups

remove unused variable in tests

remove AGPL3 or later from license choices: not in base module choices
12.0-mig-module_prototyper_last
Vincent Vinet 9 years ago
committed by Nicolas JEUDY
parent
commit
9cfa020594
  1. 1
      module_prototyper/README.rst
  2. 2
      module_prototyper/models/ir_model_fields.py
  3. 2
      module_prototyper/models/licenses.py
  4. 125
      module_prototyper/models/module_prototyper.py
  5. 9
      module_prototyper/templates/8.0/__openerp__.py.template
  6. 13
      module_prototyper/templates/8.0/data/model_name.xml.template
  7. 2
      module_prototyper/templates/8.0/models/__init__.py.template
  8. 23
      module_prototyper/templates/8.0/models/model_name.py.template
  9. 2
      module_prototyper/templates/8.0/views/model_menus.xml.template
  10. 5
      module_prototyper/tests/test_prototype_module_export.py
  11. 4
      module_prototyper/views/ir_model_fields_view.xml
  12. 40
      module_prototyper/views/module_prototyper_view.xml
  13. 50
      module_prototyper/wizard/module_prototyper_module_export.py

1
module_prototyper/README.rst

@ -65,6 +65,7 @@ Contributors
* Maxime Chambreuil <maxime.chambreuil@savoirfairelinux.com> * Maxime Chambreuil <maxime.chambreuil@savoirfairelinux.com>
* El hadji Dem <elhadji.dem@savoirfairelinux.com> * El hadji Dem <elhadji.dem@savoirfairelinux.com>
* Savoir-faire Linux <support@savoirfairelinux.com> * Savoir-faire Linux <support@savoirfairelinux.com>
* Vincent Vinet <vincent.vinet@savoirfairelinux.com>
Maintainer Maintainer
---------- ----------

2
module_prototyper/models/ir_model_fields.py

@ -42,7 +42,7 @@ class ir_model_fields(models.Model):
"relation table"), "relation table"),
) )
limit = fields.Integer('Read limit', help=_("Read limit")) limit = fields.Integer('Read limit', help=_("Read limit"))
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)"),

2
module_prototyper/models/licenses.py

@ -68,6 +68,6 @@ def get_license_text(license):
name, version = GPL_LICENSES[license] name, version = GPL_LICENSES[license]
return BASE_GPL.format(name=name, version=version).splitlines() return BASE_GPL.format(name=name, version=version).splitlines()
elif license == OSI: elif license == OSI:
return BASE_OSI
return BASE_OSI.splitlines()
else: else:
return "" return ""

125
module_prototyper/models/module_prototyper.py

@ -20,6 +20,7 @@
# #
############################################################################## ##############################################################################
import base64 import base64
import logging
import lxml.etree import lxml.etree
import os import os
import re import re
@ -31,10 +32,13 @@ from datetime import date
from jinja2 import Environment, FileSystemLoader from jinja2 import Environment, FileSystemLoader
from openerp import models, api, fields from openerp import models, api, fields
from openerp.tools.safe_eval import safe_eval
from .default_description import get_default_description from .default_description import get_default_description
from . import licenses from . import licenses
_logger = logging.getLogger(__name__)
class ModulePrototyper(models.Model): class ModulePrototyper(models.Model):
"""Module Prototyper gathers different information from all over the """Module Prototyper gathers different information from all over the
@ -53,7 +57,6 @@ class ModulePrototyper(models.Model):
(licenses.LGPL3, 'LGPL-3'), (licenses.LGPL3, 'LGPL-3'),
(licenses.LGPL3_L, 'LGPL-3 or later version'), (licenses.LGPL3_L, 'LGPL-3 or later version'),
(licenses.AGPL3, 'Affero GPL-3'), (licenses.AGPL3, 'Affero GPL-3'),
(licenses.AGPL3_L, 'Affero GPL-3 or later version'),
(licenses.OSI, 'Other OSI Approved Licence'), (licenses.OSI, 'Other OSI Approved Licence'),
('Other proprietary', 'Other Proprietary') ('Other proprietary', 'Other Proprietary')
], ],
@ -170,10 +173,29 @@ class ModulePrototyper(models.Model):
help=('Enter the list of record rules that you have created and ' help=('Enter the list of record rules that you have created and '
'want to export in this module.') 'want to export in this module.')
) )
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.')
)
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')
)
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')
)
__data_files = []
__field_descriptions = {}
_env = None _env = None
_data_files = ()
_demo_files = ()
_field_descriptions = None
File_details = namedtuple('file_details', ['filename', 'filecontent']) File_details = namedtuple('file_details', ['filename', 'filecontent'])
template_path = '{}/../templates/'.format(os.path.dirname(__file__)) template_path = '{}/../templates/'.format(os.path.dirname(__file__))
@ -211,10 +233,8 @@ class ModulePrototyper(models.Model):
for attr_name in dir(field) for attr_name in dir(field)
if not attr_name[0] == '_' 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
field_description['name'] = self.unprefix(field.name)
self._field_descriptions[field] = field_description
@api.model @api.model
def generate_files(self): def generate_files(self):
@ -224,12 +244,17 @@ class ModulePrototyper(models.Model):
assert self._env is not None, \ assert self._env is not None, \
'Run set_env(api_version) before to generate files.' 'Run set_env(api_version) before to generate files.'
# Avoid sharing these across instances
self._data_files = []
self._demo_files = []
self._field_descriptions = {}
self.set_field_descriptions() self.set_field_descriptions()
file_details = [] file_details = []
file_details.extend(self.generate_models_details()) file_details.extend(self.generate_models_details())
file_details.extend(self.generate_views_details()) file_details.extend(self.generate_views_details())
file_details.extend(self.generate_menus_details()) file_details.extend(self.generate_menus_details())
file_details.append(self.generate_module_init_file_details()) file_details.append(self.generate_module_init_file_details())
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())
@ -262,7 +287,8 @@ class ModulePrototyper(models.Model):
'__openerp__.py', '__openerp__.py',
'__openerp__.py.template', '__openerp__.py.template',
prototype=self, prototype=self,
data_files=self.__data_files,
data_files=self._data_files,
demo_fiels=self._demo_files,
) )
@api.model @api.model
@ -278,7 +304,8 @@ class ModulePrototyper(models.Model):
@api.model @api.model
def generate_models_details(self): def generate_models_details(self):
"""Finds the models from the list of fields and generates
"""
Finds the models from the list of fields and generates
the __init__ file and each models files (one by class). the __init__ file and each models files (one by class).
""" """
files = [] files = []
@ -291,7 +318,8 @@ class ModulePrototyper(models.Model):
# dependencies = set([dep.id for dep in self.dependencies]) # dependencies = set([dep.id for dep in self.dependencies])
relations = {} relations = {}
for field in self.__field_descriptions.itervalues():
field_descriptions = self._field_descriptions or {}
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)
@ -329,7 +357,7 @@ class ModulePrototyper(models.Model):
views_details = [] views_details = []
for model, views in relations.iteritems(): for model, views in relations.iteritems():
filepath = 'views/{}_view.xml'.format( filepath = 'views/{}_view.xml'.format(
self.friendly_name(model)
self.friendly_name(self.unprefix(model))
) )
views_details.append( views_details.append(
self.generate_file_details( self.generate_file_details(
@ -338,7 +366,7 @@ class ModulePrototyper(models.Model):
views=views views=views
) )
) )
self.__data_files.append(filepath)
self._data_files.append(filepath)
return views_details return views_details
@ -348,13 +376,14 @@ class ModulePrototyper(models.Model):
relations = {} relations = {}
for menu in self.menu_ids: for menu in self.menu_ids:
if menu.action and menu.action.res_model: if menu.action and menu.action.res_model:
model = 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)
filepath = 'views/{}_menus.xml'.format( filepath = 'views/{}_menus.xml'.format(
self.friendly_name(model_name) self.friendly_name(model_name)
) )
@ -365,7 +394,7 @@ class ModulePrototyper(models.Model):
menus=menus, menus=menus,
) )
) )
self.__data_files.append(filepath)
self._data_files.append(filepath)
return menus_details return menus_details
@ -377,7 +406,7 @@ class ModulePrototyper(models.Model):
:param field_descriptions: list of ir.model.fields records. :param field_descriptions: list of ir.model.fields records.
:return: FileDetails instance. :return: FileDetails instance.
""" """
python_friendly_name = self.friendly_name(model.model)
python_friendly_name = self.friendly_name(self.unprefix(model.model))
return self.generate_file_details( return self.generate_file_details(
'models/{}.py'.format(python_friendly_name), 'models/{}.py'.format(python_friendly_name),
'models/model_name.py.template', 'models/model_name.py.template',
@ -386,22 +415,87 @@ class ModulePrototyper(models.Model):
fields=field_descriptions, fields=field_descriptions,
) )
@api.model
def generate_data_files(self):
""" Generate data and demo files """
data, demo = {}, {}
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:
model = ir_filter.model_id
model_obj = self.env[model]
target.setdefault(model, model_obj.browse([]))
target[model] |= model_obj.search(safe_eval(ir_filter.domain))
res = []
for prefix, model_data, file_list in [
('data', data, self._data_files),
('demo', demo, self._demo_files)]:
for model_name, records in model_data.iteritems():
fname = self.friendly_name(self.unprefix(model_name))
filename = '{0}/{1}.xml'.format(prefix, fname)
self._data_files.append(filename)
res.append(self.generate_file_details(
filename,
'data/model_name.xml.template',
model=model_name,
records=records,
))
return res
@classmethod @classmethod
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
def is_prefixed(cls, 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
def fixup_domain(cls, domain):
""" Fix a domain according to unprefixing of fields """
res = []
for elem in domain:
if len(elem) == 3:
elem = list(elem)
elem[0] = cls.unprefix(elem[0])
res.append(elem)
return res
@classmethod @classmethod
def fixup_arch(cls, archstr): def fixup_arch(cls, archstr):
doc = lxml.etree.fromstring(archstr) doc = lxml.etree.fromstring(archstr)
for elem in doc.xpath("//*[@name]"): for elem in doc.xpath("//*[@name]"):
elem.attrib["name"] = cls.unprefix(elem.attrib["name"]) elem.attrib["name"] = cls.unprefix(elem.attrib["name"])
for elem in doc.xpath("//*[@attrs]"):
try:
attrs = safe_eval(elem.attrib["attrs"])
except Exception:
_logger.error("Unable to eval attribute: %s, skipping",
elem.attrib["attrs"])
continue
if isinstance(attrs, dict):
for key, val in attrs.iteritems():
if isinstance(val, (list, tuple)):
attrs[key] = cls.fixup_domain(val)
elem.attrib["attrs"] = repr(attrs)
for elem in doc.xpath("//field"): for elem in doc.xpath("//field"):
# Make fields self-closed by removing useless whitespace # Make fields self-closed by removing useless whitespace
if elem.text and not elem.text.strip(): if elem.text and not elem.text.strip():
@ -428,6 +522,7 @@ class ModulePrototyper(models.Model):
'cr': self._cr, 'cr': self._cr,
# Utility functions # Utility functions
'fixup_arch': self.fixup_arch, 'fixup_arch': self.fixup_arch,
'is_prefixed': self.is_prefixed,
'unprefix': self.unprefix, 'unprefix': self.unprefix,
'wrap': wrap, 'wrap': wrap,

9
module_prototyper/templates/8.0/__openerp__.py.template

@ -7,12 +7,13 @@
'author': '{{ prototype.author }}', 'author': '{{ prototype.author }}',
'maintainer': '{{ prototype.maintainer }}', 'maintainer': '{{ prototype.maintainer }}',
'website': '{{ prototype.website }}', 'website': '{{ prototype.website }}',
'license': '{{ prototype.licence }}',
'license': '{{ prototype.license }}',
# Categories can be used to filter modules in modules listing # Categories can be used to filter modules in modules listing
# Check https://github.com/odoo/odoo/blob/master/openerp/addons/base/module/module_data.xml # noqa # Check https://github.com/odoo/odoo/blob/master/openerp/addons/base/module/module_data.xml # noqa
# for the full list # for the full list
'category': '{{ prototype.with_context({}).category_id.name }}',{# In english please! #}
{# Use with_context({}) to get english category #}
'category': '{{ prototype.with_context({}).category_id.name }}',
'summary': '{{ prototype.summary }}', 'summary': '{{ prototype.summary }}',
'description': """ 'description': """
{{ prototype.description }} {{ prototype.description }}
@ -40,8 +41,8 @@
], ],
# only loaded in demonstration mode # only loaded in demonstration mode
'demo': [ 'demo': [
{% for demo_file in prototype.demo_ids %}
'{{ demo_file.name }}',
{% for demo_file in demo_files %}
'{{ demo_file }}',
{% endfor %} {% endfor %}
], ],

13
module_prototyper/templates/8.0/data/model_name.xml.template

@ -1,8 +1,17 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<openerp> <openerp>
<data> <data>
{% for record in records %}
<!--
<record id="{{ model }}_{{ loop.index }}" model="{{ model }}">
{% for key, val in record.read()[0].items() %}
<field name="{{ key }}">{{ val }}</field>
{% endfor %}
</record>
-->
{% if not loop.last %}
{{ data }}
{% endif %}
{% endfor %}
</data> </data>
</openerp> </openerp>

2
module_prototyper/templates/8.0/models/__init__.py.template

@ -4,6 +4,6 @@
{% if loop.first %} {% if loop.first %}
{% endif %} {% endif %}
from . import {{ model }}
from . import {{ unprefix(model) }}
{% endfor %} {% endfor %}
{% endblock %} {% endblock %}

23
module_prototyper/templates/8.0/models/model_name.py.template

@ -6,10 +6,10 @@ from openerp.tools.translate import _
class {{ unprefix(name) }}(models.Model): class {{ unprefix(name) }}(models.Model):
{% if model.state == 'base' %}
_name = "{{ model.model }}"
{% if model.state == 'base' and not is_prefixed(model.model) %}
_inherit = "{{ unprefix(model.model) }}"
{% else %} {% else %}
_inherit = "{{ model.model }}"
_name = "{{ unprefix(model.model) }}"
{% endif %} {% endif %}
{% if description %} {% if description %}
_description = "{{ description }}" _description = "{{ description }}"
@ -26,10 +26,13 @@ class {{ unprefix(name) }}(models.Model):
{{ unprefix(field.name) }} = fields.{{ field.ttype|capitalize }}( {{ unprefix(field.name) }} = fields.{{ field.ttype|capitalize }}(
string=_("{{ field.field_description }}"), string=_("{{ field.field_description }}"),
{% if field.selection %} {% if field.selection %}
selection={{ selection }},
selection={{ field.selection }},
{% endif %} {% endif %}
{% if field.relation %} {% if field.relation %}
comodel_name="{{ field.relation }}",
comodel_name="{{ unprefix(field.relation) }}",
{% endif %}
{% if field.ttype == 'one2many' %}
inverse_name="{{ unprefix(field.relation_field) }}",
{% endif %} {% endif %}
{% if field.column1 %} {% if field.column1 %}
column1="{{ field.column1 }}", column1="{{ field.column1 }}",
@ -43,11 +46,13 @@ class {{ unprefix(name) }}(models.Model):
{% if field.size %} {% if field.size %}
size={{ field.size }}, size={{ field.size }},
{% endif %} {% endif %}
{% if field.domain %}
{% if field.ttype in ('many2one', 'many2many', 'one2many') %}
{% if field.domain %}
domain={{ field.domain }}, domain={{ field.domain }},
{% endif %}
{% if field.context %}
context={{ field.context }},
{% endif %}
{% if field.client_context %}
context={{ field.client_context }},
{% endif %}
{% endif %} {% endif %}
{% if field.limit %} {% if field.limit %}
limit={{ field.limit }}, limit={{ field.limit }},

2
module_prototyper/templates/8.0/views/model_menus.xml.template

@ -19,7 +19,7 @@
id="menu_action_{{ unprefix(menu.name)|replace('.', '_') }}_{{ menu.action.view_type }}" id="menu_action_{{ unprefix(menu.name)|replace('.', '_') }}_{{ menu.action.view_type }}"
{% if menu.parent_id %}parent="{{ menu.parent_id.get_xml_id(cr,1,1).values()[0] }}"{% endif %} {% if menu.parent_id %}parent="{{ menu.parent_id.get_xml_id(cr,1,1).values()[0] }}"{% endif %}
sequence="{{ menu.sequence }}" sequence="{{ menu.sequence }}"
groups="{% for group in menu.groups_id %}{{ group.get_xml_id(cr,1,1).values()[0] }},{% endfor %}"
groups="{% for group in menu.groups_id %}{{ group.get_xml_id(cr,1,1).values()[0] }}{% if not loop.last %},{% endif %}{% endfor %}"
/> />
{% if not loop.last %} {% if not loop.last %}

5
module_prototyper/tests/test_prototype_module_export.py

@ -69,10 +69,7 @@ class test_prototype_module_export(common.TransactionCase):
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."""
file_details = (
('test.txt', 'generated'),
)
ret = self.main_model.zip_files(file_details)
ret = self.main_model.zip_files(self.exporter, [self.prototype])
self.assertIsInstance(ret, tuple) self.assertIsInstance(ret, tuple)
self.assertIsInstance( self.assertIsInstance(
ret.zip_file, zipfile.ZipFile ret.zip_file, zipfile.ZipFile

4
module_prototyper/views/ir_model_fields_view.xml

@ -27,8 +27,8 @@
<field name="limit" <field name="limit"
attrs="{'invisible': [('ttype', '!=', 'many2many')]}" attrs="{'invisible': [('ttype', '!=', 'many2many')]}"
/> />
<field name="context"
attrs="{'invisible': [('type', 'not in', ['many2one','one2many','many2many'])]}"
<field name="client_context"
attrs="{'invisible': [('ttype', 'not in', ['many2one','one2many','many2many'])]}"
/> />
</field> </field>
</field> </field>

40
module_prototyper/views/module_prototyper_view.xml

@ -61,13 +61,6 @@
<page string="Dependencies"> <page string="Dependencies">
<field name="dependency_ids"/> <field name="dependency_ids"/>
</page> </page>
<!--Not implemented yet-->
<!--<page string="Data &amp; Demo">-->
<!--<label for="data_ids"/>-->
<!--<field name="data_ids"/>-->
<!--<label for="demo_ids"/>-->
<!--<field name="demo_ids"/>-->
<!--</page>-->
<page string="Fields"> <page string="Fields">
<label for="field_ids"/> <label for="field_ids"/>
<field name="field_ids"/> <field name="field_ids"/>
@ -78,15 +71,30 @@
<label for="menu_ids"/> <label for="menu_ids"/>
<field name="menu_ids"/> <field name="menu_ids"/>
</page> </page>
<!--Not implemented yet-->
<!--<page string="Security">-->
<!--<label for="group_ids"/>-->
<!--<field name="group_ids"/>-->
<!--<label for="right_ids"/>-->
<!--<field name="right_ids"/>-->
<!--<label for="rule_ids"/>-->
<!--<field name="rule_ids"/>-->
<!--</page>-->
<page string="Data &amp; Demo">
<label for="data_ids"/>
<field name="data_ids"/>
<label for="demo_ids"/>
<field name="demo_ids"/>
</page>
<page string="Reports">
<label for="report_ids" />
<field name="report_ids" />
</page>
<page string="Security">
<label for="group_ids"/>
<field name="group_ids"/>
<label for="right_ids"/>
<field name="right_ids"/>
<label for="rule_ids"/>
<field name="rule_ids"/>
</page>
<page string="Workflows">
<label for="activity_ids" />
<field name="activity_ids" />
<label for="transition_ids" />
<field name="transition_ids" />
</page>
</notebook> </notebook>
</sheet> </sheet>
</form> </form>

50
module_prototyper/wizard/module_prototyper_module_export.py

@ -19,10 +19,13 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
############################################################################## ##############################################################################
import StringIO import StringIO
import base64 import base64
import os
import zipfile import zipfile
from collections import namedtuple from collections import namedtuple
from openerp import fields, models, api from openerp import fields, models, api
@ -66,24 +69,20 @@ class PrototypeModuleExport(models.TransientModel):
) )
# getting the prototype of the wizard # getting the prototype of the wizard
prototype = self.env[active_model].browse(
self._context.get('active_id')
prototypes = 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)
zip_details = self.zip_files(wizard, prototypes)
# 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())
if len(prototypes) == 1:
zip_name = prototypes[0].name
else:
zip_name = "prototyper_export"
wizard.write( wizard.write(
{ {
'name': '{}.zip'.format(prototype.name),
'name': '{}.zip'.format(zip_name),
'state': 'get', 'state': 'get',
'data': base64.encodestring(zip_details.stringIO.getvalue()) 'data': base64.encodestring(zip_details.stringIO.getvalue())
} }
@ -100,7 +99,7 @@ class PrototypeModuleExport(models.TransientModel):
} }
@staticmethod @staticmethod
def zip_files(file_details):
def zip_files(wizard, prototypes):
"""Takes a set of file and zips them. """Takes a set of file and zips them.
:param file_details: tuple (filename, file_content) :param file_details: tuple (filename, file_content)
:return: tuple (zip_file, stringIO) :return: tuple (zip_file, stringIO)
@ -109,10 +108,25 @@ class PrototypeModuleExport(models.TransientModel):
out = StringIO.StringIO() out = StringIO.StringIO()
with zipfile.ZipFile(out, 'w') as target: with zipfile.ZipFile(out, 'w') as target:
for filename, file_content in file_details:
info = zipfile.ZipInfo(filename)
info.compress_type = zipfile.ZIP_DEFLATED
info.external_attr = 2175008768 # specifies mode 0644
target.writestr(info, file_content)
for prototype in prototypes:
# 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.
file_details = prototype.generate_files()
for filename, file_content in file_details:
if isinstance(file_content, unicode):
file_content = file_content.encode('utf-8')
# Prefix all names with module technical name
filename = os.path.join(prototype.name, filename)
info = zipfile.ZipInfo(filename)
info.compress_type = zipfile.ZIP_DEFLATED
info.external_attr = 2175008768 # specifies mode 0644
target.writestr(info, file_content)
return zip_details(zip_file=target, stringIO=out) return zip_details(zip_file=target, stringIO=out)
Loading…
Cancel
Save