Skip to content

Commit a057591

Browse files
authored
Add current form columns to CSV export event if field are empty and export obsolete fields ordered after current ones (#80)
1 parent 14eb975 commit a057591

File tree

3 files changed

+63
-11
lines changed

3 files changed

+63
-11
lines changed

CHANGES.rst

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,12 @@ Changelog
44
3.2.3 (unreleased)
55
------------------
66

7-
- Nothing changed yet.
7+
- Now in the CSV export the obsolete records fields are ordered
8+
alphabetically after the current form fields.
9+
[folix-01]
10+
11+
- Add current form columns to CSV export event if field are empty.
12+
[folix-01]
813

914

1015
3.2.2 (2025-03-05)
@@ -24,8 +29,8 @@ Changelog
2429
3.2.0 (2024-11-15)
2530
------------------
2631

27-
- Added an adapter (`IDataAdapter`) to allow information to be added as a return value
28-
to the form-data expander. This allows addons that integrate information to be added
32+
- Added an adapter (`IDataAdapter`) to allow information to be added as a return value
33+
to the form-data expander. This allows addons that integrate information to be added
2934
rather than overwriting the expander each time.
3035
[mamico]
3136

src/collective/volto/formsupport/restapi/services/form_data/csv.py

Lines changed: 54 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
from collective.volto.formsupport.interfaces import IFormDataStore
2+
from collective.volto.formsupport.utils import get_blocks
3+
from copy import deepcopy
24
from io import StringIO
35
from plone.restapi.serializer.converters import json_compatible
46
from plone.restapi.services import Service
@@ -23,12 +25,37 @@ def __init__(self, context, request):
2325
block_type = block.get("@type", "")
2426
if block_type == "form":
2527
self.form_block = block
28+
self.form_block_id = id
2629

2730
if self.form_block:
2831
for field in self.form_block.get("subblocks", []):
2932
field_id = field["field_id"]
3033
self.form_fields_order.append(field_id)
3134

35+
def get_form_fields(self):
36+
blocks = get_blocks(self.context)
37+
38+
if not blocks:
39+
return {}
40+
form_block = {}
41+
for id, block in blocks.items():
42+
if id != self.form_block_id:
43+
continue
44+
block_type = block.get("@type", "")
45+
if block_type == "form":
46+
form_block = deepcopy(block)
47+
if not form_block:
48+
return {}
49+
50+
subblocks = form_block.get("subblocks", [])
51+
52+
# Add the 'custom_field_id' field back in as this isn't stored with each subblock
53+
for index, field in enumerate(subblocks):
54+
if form_block.get(field["field_id"]):
55+
subblocks[index]["custom_field_id"] = form_block.get(field["field_id"])
56+
57+
return subblocks
58+
3259
def get_ordered_keys(self, record):
3360
"""
3461
We need this method because we want to maintain the fields order set in the form.
@@ -61,37 +88,57 @@ def render(self):
6188
data = data.encode("utf-8")
6289
self.request.response.write(data)
6390

64-
def get_fields_labels(self, item):
65-
return item.attrs.get("fields_labels", {})
91+
def get_fields_labels(self):
92+
return {
93+
x["field_id"]: x.get("custom_field_id", x.get("label", x["field_id"]))
94+
for x in self.get_form_fields()
95+
if x.get("field_type", "") != "static_text"
96+
}
6697

6798
def get_data(self):
6899
store = getMultiAdapter((self.context, self.request), IFormDataStore)
69100
sbuf = StringIO()
70101
fixed_columns = ["date"]
71102
columns = []
72-
103+
legacy_columns = []
73104
rows = []
105+
fields_labels = self.get_fields_labels()
106+
74107
for item in store.search():
75108
data = {}
76-
fields_labels = self.get_fields_labels(item)
109+
77110
for k in self.get_ordered_keys(item):
78111
if k in SKIP_ATTRS:
79112
continue
113+
80114
value = item.attrs.get(k, None)
81-
label = fields_labels.get(k, k)
82-
if label not in columns and label not in fixed_columns:
83-
columns.append(label)
115+
label = {**item.attrs.get("fields_labels", {}), **fields_labels}.get(
116+
k, k
117+
)
118+
119+
if k not in self.form_fields_order and label not in legacy_columns:
120+
legacy_columns.append(label)
121+
84122
data[label] = json_compatible(value)
123+
85124
for k in fixed_columns:
86125
# add fixed columns values
87126
value = item.attrs.get(k, None)
88127
data[k] = json_compatible(value)
128+
89129
rows.append(data)
130+
131+
columns.extend(fields_labels.values())
90132
columns.extend(fixed_columns)
133+
columns.extend(sorted(legacy_columns))
134+
91135
writer = csv.DictWriter(sbuf, fieldnames=columns, quoting=csv.QUOTE_ALL)
92136
writer.writeheader()
137+
93138
for row in rows:
94139
writer.writerow(row)
140+
95141
res = sbuf.getvalue()
96142
sbuf.close()
143+
97144
return res

src/collective/volto/formsupport/tests/test_store_action_form.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ def test_store_data(self):
196196
response = self.export_csv()
197197
data = [*csv.reader(StringIO(response.text), delimiter=",")]
198198
self.assertEqual(len(data), 1)
199-
self.assertEqual(data[0], ["date"])
199+
self.assertIn("date", data[0])
200200

201201
def test_export_csv(self):
202202
self.document.blocks = {

0 commit comments

Comments
 (0)