Export: Do not rely on cached answer option values (Z#23152831) (#4225)

* Export: Do not rely on cached answer option values

* refactor duplicate code

---------

Co-authored-by: Mira Weller <weller@rami.io>
This commit is contained in:
Raphael Michel
2024-06-17 10:12:15 +02:00
committed by GitHub
parent 877401d8c0
commit 1200274ebf
2 changed files with 39 additions and 29 deletions

View File

@@ -32,7 +32,7 @@
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under the License. # License for the specific language governing permissions and limitations under the License.
from collections import OrderedDict from collections import OrderedDict, defaultdict
from decimal import Decimal from decimal import Decimal
from zoneinfo import ZoneInfo from zoneinfo import ZoneInfo
@@ -605,10 +605,9 @@ class OrderListExporter(MultiSheetListExporter):
] ]
questions = list(Question.objects.filter(event__in=self.events)) questions = list(Question.objects.filter(event__in=self.events))
options = {} options = defaultdict(list)
for q in questions: for q in questions:
if q.type == Question.TYPE_CHOICE_MULTIPLE: if q.type == Question.TYPE_CHOICE_MULTIPLE:
options[q.pk] = []
if form_data['group_multiple_choice']: if form_data['group_multiple_choice']:
for o in q.options.all(): for o in q.options.all():
options[q.pk].append(o) options[q.pk].append(o)
@@ -618,6 +617,9 @@ class OrderListExporter(MultiSheetListExporter):
headers.append(str(q.question) + ' ' + str(o.answer)) headers.append(str(q.question) + ' ' + str(o.answer))
options[q.pk].append(o) options[q.pk].append(o)
else: else:
if q.type == Question.TYPE_CHOICE:
for o in q.options.all():
options[q.pk].append(o)
headers.append(str(q.question)) headers.append(str(q.question))
headers += [ headers += [
_('Company'), _('Company'),
@@ -727,7 +729,7 @@ class OrderListExporter(MultiSheetListExporter):
for a in op.answers.all(): for a in op.answers.all():
# We do not want to localize Date, Time and Datetime question answers, as those can lead # We do not want to localize Date, Time and Datetime question answers, as those can lead
# to difficulties parsing the data (for example 2019-02-01 may become Février, 2019 01 in French). # to difficulties parsing the data (for example 2019-02-01 may become Février, 2019 01 in French).
if a.question.type == Question.TYPE_CHOICE_MULTIPLE: if a.question.type in (Question.TYPE_CHOICE_MULTIPLE, Question.TYPE_CHOICE):
acache[a.question_id] = set(o.pk for o in a.options.all()) acache[a.question_id] = set(o.pk for o in a.options.all())
elif a.question.type in Question.UNLOCALIZED_TYPES: elif a.question.type in Question.UNLOCALIZED_TYPES:
acache[a.question_id] = a.answer acache[a.question_id] = a.answer
@@ -740,6 +742,10 @@ class OrderListExporter(MultiSheetListExporter):
else: else:
for o in options[q.pk]: for o in options[q.pk]:
row.append(_('Yes') if o.pk in acache.get(q.pk, set()) else _('No')) row.append(_('Yes') if o.pk in acache.get(q.pk, set()) else _('No'))
elif q.type == Question.TYPE_CHOICE:
# Join is only necessary if the question type was modified but also keeps the code simpler here
# as we'd otherwise need some [0] and existance checks
row.append(", ".join(str(o.answer) for o in options[q.pk] if o.pk in acache.get(q.pk, set())))
else: else:
row.append(acache.get(q.pk, '')) row.append(acache.get(q.pk, ''))

View File

@@ -281,6 +281,17 @@ class TableTextRotate(Flowable):
canvas.drawString(0, -1, self.text) canvas.drawString(0, -1, self.text)
def format_answer_for_export(a):
if a.question.type in (Question.TYPE_CHOICE, Question.TYPE_CHOICE_MULTIPLE):
return ", ".join(str(o.answer) for o in a.options.all())
elif a.question.type in Question.UNLOCALIZED_TYPES:
# We do not want to localize Date, Time and Datetime question answers, as those can lead
# to difficulties parsing the data (for example 2019-02-01 may become Février, 2019 01 in French).
return a.answer
else:
return str(a)
class PDFCheckinList(ReportlabExportMixin, CheckInListMixin, BaseExporter): class PDFCheckinList(ReportlabExportMixin, CheckInListMixin, BaseExporter):
name = "overview" name = "overview"
identifier = 'checkinlistpdf' identifier = 'checkinlistpdf'
@@ -372,6 +383,14 @@ class PDFCheckinList(ReportlabExportMixin, CheckInListMixin, BaseExporter):
tdata[0].append(p) tdata[0].append(p)
qs = self._get_queryset(cl, form_data) qs = self._get_queryset(cl, form_data)
qs = qs.prefetch_related(
'answers',
'answers__options',
'answers__question',
'addon_to__answers',
'addon_to__answers__question',
'addon_to__answers__options',
)
for op in qs: for op in qs:
try: try:
@@ -413,19 +432,9 @@ class PDFCheckinList(ReportlabExportMixin, CheckInListMixin, BaseExporter):
acache = {} acache = {}
if op.addon_to: if op.addon_to:
for a in op.addon_to.answers.all(): for a in op.addon_to.answers.all():
# We do not want to localize Date, Time and Datetime question answers, as those can lead acache[a.question_id] = format_answer_for_export(a)
# to difficulties parsing the data (for example 2019-02-01 may become Février, 2019 01 in French).
if a.question.type in Question.UNLOCALIZED_TYPES:
acache[a.question_id] = a.answer
else:
acache[a.question_id] = str(a)
for a in op.answers.all(): for a in op.answers.all():
# We do not want to localize Date, Time and Datetime question answers, as those can lead acache[a.question_id] = format_answer_for_export(a)
# to difficulties parsing the data (for example 2019-02-01 may become Février, 2019 01 in French).
if a.question.type in Question.UNLOCALIZED_TYPES:
acache[a.question_id] = a.answer
else:
acache[a.question_id] = str(a)
for q in questions: for q in questions:
txt = acache.get(q.pk, '') txt = acache.get(q.pk, '')
txt = bleach.clean(txt, tags=['br']).strip().replace('<br>', '<br/>') txt = bleach.clean(txt, tags=['br']).strip().replace('<br>', '<br/>')
@@ -525,7 +534,12 @@ class CSVCheckinList(CheckInListMixin, ListExporter):
yield headers yield headers
qs = base_qs.prefetch_related( qs = base_qs.prefetch_related(
'answers', 'answers__question', 'addon_to__answers', 'addon_to__answers__question' 'answers',
'answers__options',
'answers__question',
'addon_to__answers',
'addon_to__answers__question',
'addon_to__answers__options',
) )
all_ids = list(base_qs.values_list('pk', flat=True)) all_ids = list(base_qs.values_list('pk', flat=True))
@@ -597,19 +611,9 @@ class CSVCheckinList(CheckInListMixin, ListExporter):
acache = {} acache = {}
if op.addon_to: if op.addon_to:
for a in op.addon_to.answers.all(): for a in op.addon_to.answers.all():
# We do not want to localize Date, Time and Datetime question answers, as those can lead acache[a.question_id] = format_answer_for_export(a)
# to difficulties parsing the data (for example 2019-02-01 may become Février, 2019 01 in French).
if a.question.type in Question.UNLOCALIZED_TYPES:
acache[a.question_id] = a.answer
else:
acache[a.question_id] = str(a)
for a in op.answers.all(): for a in op.answers.all():
# We do not want to localize Date, Time and Datetime question answers, as those can lead acache[a.question_id] = format_answer_for_export(a)
# to difficulties parsing the data (for example 2019-02-01 may become Février, 2019 01 in French).
if a.question.type in Question.UNLOCALIZED_TYPES:
acache[a.question_id] = a.answer
else:
acache[a.question_id] = str(a)
for q in questions: for q in questions:
row.append(acache.get(q.pk, '')) row.append(acache.get(q.pk, ''))