From 8ab1038c6f93d8196a44839d3226d3d6794e8f2e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20P=C3=A9ters?= <fpeters@entrouvert.com>
Date: Sat, 5 Dec 2020 15:45:30 +0100
Subject: [PATCH 5/5] backoffice: convert csv/ods exports to afterjob class
 (#48407)

---
 wcs/backoffice/management.py | 198 +++++++++++++++++++----------------
 1 file changed, 106 insertions(+), 92 deletions(-)

diff --git a/wcs/backoffice/management.py b/wcs/backoffice/management.py
index f9847544a..f62afc100 100644
--- a/wcs/backoffice/management.py
+++ b/wcs/backoffice/management.py
@@ -1992,23 +1992,6 @@ class FormPage(Directory):
         r += htmltext('</div>')
         return r.getvalue()
 
-    def csv_tuple_heading(self, fields):
-        heading_fields = [] # '#id', _('time'), _('userlabel'), _('status')]
-        for field in fields:
-            heading_fields.extend(field.get_csv_heading())
-        return heading_fields
-
-    def get_spreadsheet_line(self, fields, data):
-        elements = []
-        for field in fields:
-            element = data.get_field_view_value(field) or ''
-            display_value = None
-            if field.store_display_value:
-                display_value = data.data.get('%s_display' % field.id) or ''
-            for value in field.get_csv_value(element, display_value=display_value):
-                elements.append({'field': field, 'value': value, 'native_value': element})
-        return elements
-
     def csv(self):
         self.check_access()
         fields = self.get_fields_from_query()
@@ -2018,50 +2001,28 @@ class FormPage(Directory):
         criterias = self.get_criterias_from_query()
         order_by = misc.get_order_by_or_400(get_request().form.get('order_by', None))
 
-        class Exporter(object):
-            def __init__(self, formpage, formdef, fields, selected_filter):
-                self.formpage = formpage
-                self.formdef = formdef
-                self.fields = fields
-                self.selected_filter = selected_filter
-
-            def export(self, job=None):
-                self.output = StringIO()
-                csv_output = csv.writer(self.output)
-
-                csv_output.writerow(self.formpage.csv_tuple_heading(self.fields))
-
-                items, total_count = FormDefUI(self.formdef).get_listing_items(
-                        fields, self.selected_filter, user=user, query=query,
-                        criterias=criterias, order_by=order_by)
-
-                for filled in items:
-                    csv_output.writerow(tuple(
-                        [x['value'] for x in self.formpage.get_spreadsheet_line(self.fields, filled)]))
-
-                if job:
-                    job.file_content = self.output.getvalue()
-                    job.content_type = 'text/csv'
-                    job.store()
-
         get_logger().info('backoffice - form %s - listing csv' % self.formdef.name)
 
         count = self.formdef.data_class().count()
-        exporter = Exporter(self, self.formdef, fields, selected_filter)
+        job = CsvExportAfterJob(
+                self.formdef,
+                fields=fields,
+                selected_filter=selected_filter,
+                user_id=user.id,
+                query=query,
+                criterias=criterias,
+                order_by=order_by)
         if count > self.WCS_SYNC_EXPORT_LIMIT:
-            job = get_response().add_after_job(
-                str(N_('Exporting forms in CSV')),
-                exporter.export)
-            job.file_name = '%s.csv' % self.formdef.url_name
+            job = get_response().add_after_job(job)
             job.store()
             return redirect('export?job=%s' % job.id)
         else:
-            exporter.export()
+            job.execute()
 
             response = get_response()
             response.set_content_type('text/plain')
             #response.set_header('content-disposition', 'attachment; filename=%s.csv' % self.formdef.url_name)
-            return exporter.output.getvalue()
+            return job.file_content
 
     def export(self):
         self.check_access()
@@ -2120,57 +2081,26 @@ class FormPage(Directory):
         criterias = self.get_criterias_from_query()
         order_by = misc.get_order_by_or_400(get_request().form.get('order_by', None))
 
-        class Exporter(object):
-            def __init__(self, formpage, formdef, fields, selected_filter):
-                self.formpage = formpage
-                self.formdef = formdef
-                self.fields = fields
-                self.selected_filter = selected_filter
-
-            def export(self, job=None):
-                w = ods.Workbook(encoding=get_publisher().site_charset)
-                ws = w.add_sheet(self.formdef.name)
-
-                for i, f in enumerate(self.formpage.csv_tuple_heading(self.fields)):
-                    ws.write(0, i, f)
-
-                items, total_count = FormDefUI(self.formdef).get_listing_items(
-                        fields, self.selected_filter, user=user, query=query,
-                        criterias=criterias, order_by=order_by)
-
-                for i, formdata in enumerate(items):
-                    for j, item in enumerate(self.formpage.get_spreadsheet_line(fields, formdata)):
-                        ws.write(i+1, j, item['value'],
-                                formdata=formdata,
-                                data_field=item['field'],
-                                native_value=item['native_value'])
-
-                self.output = BytesIO()
-                w.save(self.output)
-
-                if job:
-                    job.file_content = self.output.getvalue()
-                    job.content_type = 'application/vnd.oasis.opendocument.spreadsheet'
-                    job.store()
-
-        get_logger().info('backoffice - form %s - as ods' % self.formdef.name)
-
         count = self.formdef.data_class().count()
-        exporter = Exporter(self, self.formdef, fields, selected_filter)
+        job = OdsExportAfterJob(
+                self.formdef,
+                fields=fields,
+                selected_filter=selected_filter,
+                user_id=user.id,
+                query=query,
+                criterias=criterias,
+                order_by=order_by)
         if count > self.WCS_SYNC_EXPORT_LIMIT and not get_request().is_api_url():
-            job = get_response().add_after_job(
-                str(N_('Exporting forms in Open Document format')),
-                exporter.export)
-            job.file_name = '%s.ods' % self.formdef.url_name
+            job = get_response().add_after_job(job)
             job.store()
             return redirect('export?job=%s' % job.id)
         else:
-            exporter.export()
+            job.execute()
 
             response = get_response()
             response.set_content_type('application/vnd.oasis.opendocument.spreadsheet')
             response.set_header('content-disposition', 'attachment; filename=%s.ods' % self.formdef.url_name)
-            return exporter.output.getvalue()
+            return job.file_content
 
     def json(self):
         anonymise = 'anonymise' in get_request().form
@@ -3488,3 +3418,87 @@ class MassActionAfterJob(AfterJob):
                 formdata.perform_global_action(action['action'].id, user)
             self.completion_status = '{}/{}'.format(i + 1, len(formdatas))
             self.store()
+
+
+class CsvExportAfterJob(AfterJob):
+    label = N_('Exporting forms in CSV')
+
+    def __init__(self, formdef, **kwargs):
+        super().__init__(
+            formdef_class=formdef.__class__,
+            formdef_id=formdef.id,
+            **kwargs)
+        self.file_name = '%s.csv' % formdef.url_name
+
+    def csv_tuple_heading(self, fields):
+        heading_fields = []  # '#id', _('time'), _('userlabel'), _('status')]
+        for field in fields:
+            heading_fields.extend(field.get_csv_heading())
+        return heading_fields
+
+    def get_spreadsheet_line(self, fields, data):
+        elements = []
+        for field in fields:
+            element = data.get_field_view_value(field) or ''
+            display_value = None
+            if field.store_display_value:
+                display_value = data.data.get('%s_display' % field.id) or ''
+            for value in field.get_csv_value(element, display_value=display_value):
+                elements.append({'field': field, 'value': value, 'native_value': element})
+        return elements
+
+    def execute(self):
+        formdef = self.kwargs['formdef_class'].get(self.kwargs['formdef_id'])
+        selected_filter = self.kwargs['selected_filter']
+        fields = self.kwargs['fields']
+        query = self.kwargs['query']
+        criterias = self.kwargs['criterias']
+        order_by = self.kwargs['order_by']
+        user = get_publisher().user_class.get(self.kwargs['user_id'])
+
+        items, total_count = FormDefUI(formdef).get_listing_items(
+                fields, selected_filter, user=user, query=query,
+                criterias=criterias, order_by=order_by)
+
+        return self.create_export(formdef, fields, items, total_count)
+
+    def create_export(self, formdef, fields, items, total_count):
+        output = StringIO()
+        csv_output = csv.writer(output)
+
+        csv_output.writerow(self.csv_tuple_heading(fields))
+
+        for filled in items:
+            csv_output.writerow(tuple(
+                [x['value'] for x in self.get_spreadsheet_line(fields, filled)]))
+
+        self.file_content = output.getvalue()
+        self.content_type = 'text/csv'
+        self.store()
+
+
+class OdsExportAfterJob(CsvExportAfterJob):
+    def __init__(self, formdef, **kwargs):
+        super().__init__(formdef=formdef, **kwargs)
+        self.file_name = '%s.ods' % formdef.url_name
+
+    def create_export(self, formdef, fields, items, total_count):
+        workbook = ods.Workbook(encoding='utf-8')
+        ws = workbook.add_sheet(formdef.name)
+
+        for i, field in enumerate(self.csv_tuple_heading(fields)):
+            ws.write(0, i, field)
+
+        for i, formdata in enumerate(items):
+            for j, item in enumerate(self.get_spreadsheet_line(fields, formdata)):
+                ws.write(i + 1, j, item['value'],
+                        formdata=formdata,
+                        data_field=item['field'],
+                        native_value=item['native_value'])
+
+        output = BytesIO()
+        workbook.save(output)
+
+        self.file_content = output.getvalue()
+        self.content_type = 'application/vnd.oasis.opendocument.spreadsheet'
+        self.store()
-- 
2.29.2

