mirror of
https://github.com/wagtail/wagtail.git
synced 2024-12-01 11:41:20 +01:00
Change order of export fields & Update mixins
Makes the export file easier to import as the first two columns are used by default in the import command
This commit is contained in:
parent
07adb156d1
commit
c5a6eded96
@ -142,6 +142,9 @@ class TestLockedPagesView(TestCase, WagtailTestUtils):
|
||||
)
|
||||
self.assertEqual(len(cell_array), 2)
|
||||
|
||||
self.assertEqual(worksheet["B2"].number_format, ExcelDateFormatter().get())
|
||||
self.assertEqual(worksheet["E2"].number_format, ExcelDateFormatter().get())
|
||||
|
||||
|
||||
class TestFilteredLockedPagesView(TestCase, WagtailTestUtils):
|
||||
fixtures = ["test.json"]
|
||||
@ -407,6 +410,8 @@ class TestAgingPagesView(TestCase, WagtailTestUtils):
|
||||
)
|
||||
self.assertEqual(len(cell_array), 2)
|
||||
|
||||
self.assertEqual(worksheet["C2"].number_format, ExcelDateFormatter().get())
|
||||
|
||||
|
||||
class TestFilteredAgingPagesView(TestCase, WagtailTestUtils):
|
||||
fixtures = ["test.json"]
|
||||
|
@ -1,13 +1,16 @@
|
||||
import csv
|
||||
import datetime
|
||||
from collections import OrderedDict
|
||||
from io import BytesIO
|
||||
|
||||
from django.core.exceptions import FieldDoesNotExist
|
||||
from django.http import HttpResponse, StreamingHttpResponse
|
||||
from django.http import FileResponse, StreamingHttpResponse
|
||||
from django.utils import timezone
|
||||
from django.utils.dateformat import Formatter
|
||||
from django.utils.encoding import force_str
|
||||
from django.utils.formats import get_format
|
||||
from xlsxwriter.workbook import Workbook
|
||||
from openpyxl import Workbook
|
||||
from openpyxl.cell import WriteOnlyCell
|
||||
|
||||
from wagtail.coreutils import multigetattr
|
||||
|
||||
@ -136,6 +139,13 @@ class SpreadsheetExportMixin:
|
||||
custom_field_preprocess = {}
|
||||
# A dictionary of preprocessing functions by value class and format
|
||||
custom_value_preprocess = {
|
||||
datetime.datetime: {
|
||||
FORMAT_XLSX: lambda value: (
|
||||
value
|
||||
if timezone.is_naive(value)
|
||||
else timezone.make_naive(value, timezone.utc)
|
||||
)
|
||||
},
|
||||
(datetime.date, datetime.time): {FORMAT_XLSX: None},
|
||||
list: {FORMAT_CSV: list_to_str, FORMAT_XLSX: list_to_str},
|
||||
}
|
||||
@ -169,27 +179,31 @@ class SpreadsheetExportMixin:
|
||||
# Finally resort to force_str to prevent encoding errors
|
||||
return force_str
|
||||
|
||||
def write_xlsx_row(self, worksheet, row_dict, row_number):
|
||||
for col_number, (field, value) in enumerate(row_dict.items()):
|
||||
preprocess_function = self.get_preprocess_function(
|
||||
field, value, self.FORMAT_XLSX
|
||||
def preprocess_field_value(self, field, value, export_format):
|
||||
"""Preprocesses a field value before writing it to the spreadsheet"""
|
||||
preprocess_function = self.get_preprocess_function(field, value, export_format)
|
||||
if preprocess_function is not None:
|
||||
return preprocess_function(value)
|
||||
else:
|
||||
return value
|
||||
|
||||
def generate_xlsx_row(self, worksheet, row_dict, date_format=None):
|
||||
"""Generate cells to append to the worksheet"""
|
||||
for field, value in row_dict.items():
|
||||
cell = WriteOnlyCell(
|
||||
worksheet, self.preprocess_field_value(field, value, self.FORMAT_XLSX)
|
||||
)
|
||||
processed_value = (
|
||||
preprocess_function(value) if preprocess_function else value
|
||||
)
|
||||
worksheet.write(row_number, col_number, processed_value)
|
||||
if date_format and isinstance(value, datetime.datetime):
|
||||
cell.number_format = date_format
|
||||
yield cell
|
||||
|
||||
def write_csv_row(self, writer, row_dict):
|
||||
processed_row = {}
|
||||
for field, value in row_dict.items():
|
||||
preprocess_function = self.get_preprocess_function(
|
||||
field, value, self.FORMAT_CSV
|
||||
)
|
||||
processed_value = (
|
||||
preprocess_function(value) if preprocess_function else value
|
||||
)
|
||||
processed_row[field] = processed_value
|
||||
return writer.writerow(processed_row)
|
||||
return writer.writerow(
|
||||
{
|
||||
field: self.preprocess_field_value(field, value, self.FORMAT_CSV)
|
||||
for field, value in row_dict.items()
|
||||
}
|
||||
)
|
||||
|
||||
def get_heading(self, queryset, field):
|
||||
"""Get the heading label for a given field for a spreadsheet generated from queryset"""
|
||||
@ -213,35 +227,35 @@ class SpreadsheetExportMixin:
|
||||
|
||||
def write_xlsx(self, queryset, output):
|
||||
"""Write an xlsx workbook from a queryset"""
|
||||
workbook = Workbook(
|
||||
output,
|
||||
{
|
||||
"in_memory": True,
|
||||
"constant_memory": True,
|
||||
"remove_timezone": True,
|
||||
"default_date_format": ExcelDateFormatter().get(),
|
||||
},
|
||||
workbook = Workbook(write_only=True, iso_dates=True)
|
||||
|
||||
worksheet = workbook.create_sheet(title="Sheet1")
|
||||
worksheet.append(
|
||||
self.get_heading(queryset, field) for field in self.list_export
|
||||
)
|
||||
worksheet = workbook.add_worksheet()
|
||||
|
||||
for col_number, field in enumerate(self.list_export):
|
||||
worksheet.write(0, col_number, self.get_heading(queryset, field))
|
||||
date_format = ExcelDateFormatter().get()
|
||||
for item in queryset:
|
||||
worksheet.append(
|
||||
self.generate_xlsx_row(
|
||||
worksheet, self.to_row_dict(item), date_format=date_format
|
||||
)
|
||||
)
|
||||
|
||||
for row_number, item in enumerate(queryset):
|
||||
self.write_xlsx_row(worksheet, self.to_row_dict(item), row_number + 1)
|
||||
|
||||
workbook.close()
|
||||
workbook.save(output)
|
||||
|
||||
def write_xlsx_response(self, queryset):
|
||||
response = HttpResponse(
|
||||
content_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
||||
)
|
||||
response["Content-Disposition"] = 'attachment; filename="{}.xlsx"'.format(
|
||||
self.get_filename()
|
||||
)
|
||||
self.write_xlsx(queryset, response)
|
||||
"""Write an xlsx file from a queryset and return a FileResponse"""
|
||||
output = BytesIO()
|
||||
self.write_xlsx(queryset, output)
|
||||
output.seek(0)
|
||||
|
||||
return response
|
||||
return FileResponse(
|
||||
output,
|
||||
as_attachment=True,
|
||||
content_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
filename=f"{self.get_filename()}.xlsx",
|
||||
)
|
||||
|
||||
def write_csv_response(self, queryset):
|
||||
stream = self.stream_csv(queryset)
|
||||
|
@ -63,9 +63,9 @@ class TestRedirectReport(TestCase, WagtailTestUtils):
|
||||
csv_entries = csv_data[1:]
|
||||
csv_entries = csv_entries[:-1] # Drop empty last line
|
||||
|
||||
self.assertEqual(csv_header, "From,Site,To,Type\r")
|
||||
self.assertEqual(csv_header, "From,To,Type,Site\r")
|
||||
self.assertEqual(len(csv_entries), 1)
|
||||
self.assertEqual(csv_entries[0], "/from,None,/to,temporary\r")
|
||||
self.assertEqual(csv_entries[0], "/from,/to,temporary,None\r")
|
||||
|
||||
def test_xlsx_export(self):
|
||||
Redirect.add_redirect("/from", "/to", True)
|
||||
@ -77,6 +77,6 @@ class TestRedirectReport(TestCase, WagtailTestUtils):
|
||||
worksheet = load_workbook(filename=BytesIO(workbook_data))["Sheet1"]
|
||||
cell_array = [[cell.value for cell in row] for row in worksheet.rows]
|
||||
|
||||
self.assertEqual(cell_array[0], ["From", "Site", "To", "Type"])
|
||||
self.assertEqual(cell_array[0], ["From", "To", "Type", "Site"])
|
||||
self.assertEqual(len(cell_array), 2)
|
||||
self.assertEqual(cell_array[1], ["/from", "None", "/to", "permanent"])
|
||||
self.assertEqual(cell_array[1], ["/from", "/to", "permanent", "None"])
|
||||
|
@ -411,9 +411,9 @@ class RedirectsReportView(ReportView):
|
||||
|
||||
list_export = [
|
||||
"old_path",
|
||||
"site",
|
||||
"link",
|
||||
"get_is_permanent_display",
|
||||
"site",
|
||||
]
|
||||
|
||||
export_headings = {
|
||||
|
Loading…
Reference in New Issue
Block a user