changeset 6abb3942043f in trytond:default
details: https://hg.tryton.org/trytond?cmd=changeset&node=6abb3942043f
description:
        Add header parameter on export data

        issue9301
        review439151003
diffstat:

 CHANGELOG                        |   1 +
 doc/ref/models.rst               |   5 ++-
 trytond/ir/export.py             |   7 ++++++
 trytond/ir/message.xml           |   6 +++++
 trytond/ir/routes.py             |   6 +---
 trytond/ir/view/export_form.xml  |   4 ++-
 trytond/model/modelstorage.py    |  41 ++++++++++++++++++++++++++++------
 trytond/model/modelview.py       |   2 +-
 trytond/tests/test_exportdata.py |  46 ++++++++++++++++++++++++++++++++++++++++
 trytond/tests/test_routes.py     |  13 ++++++-----
 10 files changed, 109 insertions(+), 22 deletions(-)

diffs (293 lines):

diff -r f2213e8ce5b2 -r 6abb3942043f CHANGELOG
--- a/CHANGELOG Tue Jun 21 10:57:08 2022 +0200
+++ b/CHANGELOG Fri Jul 01 12:45:38 2022 +0200
@@ -1,3 +1,4 @@
+* Add header parameter on export data
 * Enforce certificate validation for SMTP connection (issue11564)
 * Use singleton for TableHandler
 
diff -r f2213e8ce5b2 -r 6abb3942043f doc/ref/models.rst
--- a/doc/ref/models.rst        Tue Jun 21 10:57:08 2022 +0200
+++ b/doc/ref/models.rst        Fri Jul 01 12:45:38 2022 +0200
@@ -413,18 +413,19 @@
 
    Return a list of record instance for the ``ids``.
 
-.. classmethod:: ModelStorage.export_data(records, fields_names)
+.. classmethod:: ModelStorage.export_data(records, fields_names[, header])
 
    Return a list of list of values for each ``records``.
 
    The list of values follows ``fields_names``.
+   The result includes the description of the fields if ``header`` is set.
 
    Relational fields are defined with ``/`` at any depth.
 
    Descriptor on fields are available by appending ``.`` and the name of the
    method on the field that returns the descriptor.
 
-.. classmethod:: ModelStorage.export_data_domain(domain, fields_names[, 
offset[, limit[, order]]])
+.. classmethod:: ModelStorage.export_data_domain(domain, fields_names[, 
offset[, limit[, order[, header]]]])
 
    Call :meth:`search` and :meth:`export_data` together.
 
diff -r f2213e8ce5b2 -r 6abb3942043f trytond/ir/export.py
--- a/trytond/ir/export.py      Tue Jun 21 10:57:08 2022 +0200
+++ b/trytond/ir/export.py      Fri Jul 01 12:45:38 2022 +0200
@@ -28,6 +28,9 @@
     __name__ = "ir.export"
     name = fields.Char('Name')
     resource = fields.Char('Resource')
+    header = fields.Boolean(
+        "Header",
+        help="Check to include field names on the export.")
     export_fields = fields.One2Many('ir.export.line', 'export',
        'Fields')
 
@@ -38,6 +41,10 @@
             update=RPC(instantiate=0, readonly=False))
 
     @classmethod
+    def default_header(cls):
+        return False
+
+    @classmethod
     def update(cls, exports, fields):
         pool = Pool()
         Line = pool.get('ir.export.line')
diff -r f2213e8ce5b2 -r 6abb3942043f trytond/ir/message.xml
--- a/trytond/ir/message.xml    Tue Jun 21 10:57:08 2022 +0200
+++ b/trytond/ir/message.xml    Fri Jul 01 12:45:38 2022 +0200
@@ -390,5 +390,11 @@
         <record model="ir.message" id="msg_view_search_invalid_domain">
             <field name="text">Invalid domain or search criteria "%(domain)s" 
for search "%(search)s".</field>
         </record>
+        <record model="ir.message" id="msg_field_string">
+            <field name="text">%(field)s (string)</field>
+        </record>
+        <record model="ir.message" id="msg_field_model_name">
+            <field name="text">%(field)s (model name)</field>
+        </record>
     </data>
 </tryton>
diff -r f2213e8ce5b2 -r 6abb3942043f trytond/ir/routes.py
--- a/trytond/ir/routes.py      Tue Jun 21 10:57:08 2022 +0200
+++ b/trytond/ir/routes.py      Fri Jul 01 12:45:38 2022 +0200
@@ -260,17 +260,15 @@
 
         try:
             if domain and isinstance(domain[0], (int, float)):
-                rows = Model.export_data(domain, fields_names)
+                rows = Model.export_data(domain, fields_names, header)
             else:
                 rows = Model.export_data_domain(
                     domain, fields_names,
-                    limit=limit, offset=offset, order=order)
+                    limit=limit, offset=offset, order=order, header=header)
         except (ValueError, KeyError):
             abort(HTTPStatus.BAD_REQUEST)
         data = io.StringIO(newline='')
         writer = csv.writer(data, delimiter=delimiter, quotechar=quotechar)
-        if header:
-            writer.writerow(fields_names)
         for row in rows:
             writer.writerow(format_(row))
         data = data.getvalue().encode(encoding)
diff -r f2213e8ce5b2 -r 6abb3942043f trytond/ir/view/export_form.xml
--- a/trytond/ir/view/export_form.xml   Tue Jun 21 10:57:08 2022 +0200
+++ b/trytond/ir/view/export_form.xml   Fri Jul 01 12:45:38 2022 +0200
@@ -3,8 +3,10 @@
 this repository contains the full copyright notices and license terms. -->
 <form>
     <label name="name"/>
-    <field name="name"/>
+    <field name="name" colspan="3"/>
     <label name="resource"/>
     <field name="resource"/>
+    <label name="header"/>
+    <field name="header"/>
     <field name="export_fields" colspan="4"/>
 </form>
diff -r f2213e8ce5b2 -r 6abb3942043f trytond/model/modelstorage.py
--- a/trytond/model/modelstorage.py     Tue Jun 21 10:57:08 2022 +0200
+++ b/trytond/model/modelstorage.py     Fri Jul 01 12:45:38 2022 +0200
@@ -738,23 +738,48 @@
         return [data] + lines
 
     @classmethod
-    def export_data(cls, records, fields_names):
-        '''
-        Return list of list of values for each record.
-        The list of values follows fields_names.
-        Relational fields are defined with '/' at any depth.
-        '''
+    def _convert_field_names(cls, fields_names):
+        pool = Pool()
+        ModelField = pool.get('ir.model.field')
+        result = []
+        for names in fields_names:
+            descriptions = []
+            class_ = cls
+            for i, name in enumerate(names):
+                translated = name.endswith('.translated')
+                if translated:
+                    name = name[:-len('.translated')]
+                field = class_._fields[name]
+                field_name = ModelField.get_name(class_.__name__, name)
+                if translated:
+                    if isinstance(field, fields.Selection):
+                        field_name = gettext(
+                            'ir.msg_field_string', field=field_name)
+                    elif isinstance(field, fields.Reference):
+                        field_name = gettext(
+                            'ir.msg_field_model_name', field=field_name)
+                descriptions.append(field_name)
+                if hasattr(field, 'get_target'):
+                    class_ = field.get_target()
+            result.append('/'.join(descriptions))
+        return result
+
+    @classmethod
+    def export_data(cls, records, fields_names, header=False):
         fields_names = [x.split('/') for x in fields_names]
         data = []
+        if header:
+            data.append(cls._convert_field_names(fields_names))
         for record in records:
             data += cls.__export_row(record, fields_names)
         return data
 
     @classmethod
     def export_data_domain(
-            cls, domain, fields_names, offset=0, limit=None, order=None):
+            cls, domain, fields_names, offset=0, limit=None, order=None,
+            header=False):
         records = cls.search(domain, limit=limit, offset=offset, order=order)
-        return cls.export_data(records, fields_names)
+        return cls.export_data(records, fields_names, header=header)
 
     @classmethod
     def import_data(cls, fields_names, data):
diff -r f2213e8ce5b2 -r 6abb3942043f trytond/model/modelview.py
--- a/trytond/model/modelview.py        Tue Jun 21 10:57:08 2022 +0200
+++ b/trytond/model/modelview.py        Fri Jul 01 12:45:38 2022 +0200
@@ -299,7 +299,7 @@
         relates = Action.get_keyword('form_relate', (cls.__name__, -1))
         exports = Export.search_read(
             [('resource', '=', cls.__name__)],
-            fields_names=['name', 'export_fields.name'])
+            fields_names=['name', 'header', 'export_fields.name'])
         emails = Email.search_read(
             [('model.model', '=', cls.__name__)],
             fields_names=['name'])
diff -r f2213e8ce5b2 -r 6abb3942043f trytond/tests/test_exportdata.py
--- a/trytond/tests/test_exportdata.py  Tue Jun 21 10:57:08 2022 +0200
+++ b/trytond/tests/test_exportdata.py  Fri Jul 01 12:45:38 2022 +0200
@@ -404,3 +404,49 @@
             ExportData.export_data_domain(
                 [('boolean', '=', True)], ['boolean']),
             [[True]])
+
+    @with_transaction()
+    def test_header(self):
+        "Test export data with header"
+        pool = Pool()
+        ExportData = pool.get('test.export_data')
+
+        export1, = ExportData.create([{
+                    'char': "Test",
+                    'integer': 2,
+                    }])
+        self.assertEqual(
+            ExportData.export_data([export1], ['char', 'integer'], True),
+            [["Char", "Integer"], ["Test", 2]])
+
+    @with_transaction()
+    def test_nested_header(self):
+        "Test export data with header and nested fields"
+        pool = Pool()
+        ExportData = pool.get('test.export_data')
+
+        fields_names = [
+            'many2one/name', 'many2one/rec_name', 'selection',
+            'selection.translated', 'reference.translated',
+            'reference/rec_name']
+        self.assertEqual(
+            ExportData.export_data([], fields_names, True),
+            [["Many2One/Name", "Many2One/Record Name", "Selection",
+                "Selection (string)", "Reference (model name)",
+                "Reference/Record Name"]])
+
+    @with_transaction()
+    def test_header_domain(self):
+        "Test export data with header and domain"
+        pool = Pool()
+        ExportData = pool.get('test.export_data')
+        ExportData.create([{
+                    'boolean': True,
+                    }, {
+                    'boolean': False,
+                    }])
+
+        self.assertEqual(
+            ExportData.export_data_domain(
+                [('boolean', '=', True)], ['boolean'], header=True),
+            [["Boolean"], [True]])
diff -r f2213e8ce5b2 -r 6abb3942043f trytond/tests/test_routes.py
--- a/trytond/tests/test_routes.py      Tue Jun 21 10:57:08 2022 +0200
+++ b/trytond/tests/test_routes.py      Fri Jul 01 12:45:38 2022 +0200
@@ -62,7 +62,7 @@
             query_string=[('f', 'name')])
 
         self.assertEqual(response.status_code, 200)
-        self.assertEqual(response.data, b'name\r\nAdministrator\r\n')
+        self.assertEqual(response.data, b'Nom\r\nAdministrator\r\n')
 
     def test_data_multiple_fields(self):
         "Test GET data with multiple fields"
@@ -74,7 +74,7 @@
 
         self.assertEqual(response.status_code, 200)
         self.assertEqual(
-            response.data, b'name,login\r\nAdministrator,admin\r\n')
+            response.data, b'Nom,Identifiant\r\nAdministrator,admin\r\n')
 
     def test_data_language(self):
         "Test GET data with language"
@@ -89,7 +89,7 @@
                 ])
 
         self.assertEqual(response.status_code, 200)
-        self.assertEqual(response.data, 'name\r\nFrançais\r\n'.encode('utf-8'))
+        self.assertEqual(response.data, 'Nom\r\nFrançais\r\n'.encode('utf-8'))
 
     def test_data_size(self):
         "Test GET data with size limit"
@@ -143,7 +143,7 @@
 
         self.assertEqual(response.status_code, 200)
         self.assertEqual(
-            response.data, 'name\r\nFrançais\r\n'.encode('latin1'))
+            response.data, 'Nom\r\nFrançais\r\n'.encode('latin1'))
 
     def test_data_delimiter(self):
         "Test GET data with delimiter"
@@ -155,7 +155,7 @@
 
         self.assertEqual(response.status_code, 200)
         self.assertEqual(
-            response.data, b'name|login\r\nAdministrator|admin\r\n')
+            response.data, b'Nom|Identifiant\r\nAdministrator|admin\r\n')
 
     def test_data_quotechar(self):
         "Test GET data with quotechar"
@@ -168,7 +168,8 @@
 
         self.assertEqual(response.status_code, 200)
         self.assertEqual(
-            response.data, b'*name*n*login*\r\n*Administrator*n*admin*\r\n')
+            response.data,
+            b'Nomn*Identifiant*\r\n*Administrator*n*admin*\r\n')
 
     def test_data_no_header(self):
         "Test GET data without header"

Reply via email to