diff --git a/app/ModelSerializers/Summit/Registration/SponsorBadgeScanCSVSerializer.php b/app/ModelSerializers/Summit/Registration/SponsorBadgeScanCSVSerializer.php index f6e399afe..16117ddc8 100644 --- a/app/ModelSerializers/Summit/Registration/SponsorBadgeScanCSVSerializer.php +++ b/app/ModelSerializers/Summit/Registration/SponsorBadgeScanCSVSerializer.php @@ -52,6 +52,31 @@ private static function isAllowedExtraQuestion(array $allowed_extra_questions, i return false; } + /** + * Returns a CSV column key that does not collide with the already taken ones, + * disambiguating with the question name, then the id, then a counter as last resort. + * @param array $taken_keys + * @param string $label + * @param string $name + * @param int $id + * @return string + */ + private static function getUniqueColumnKey(array $taken_keys, string $label, string $name, int $id): string + { + if (!array_key_exists($label, $taken_keys)) return $label; + + $key = sprintf("%s (%s)", $label, AbstractSerializer::getCSVLabel($name)); + if (!array_key_exists($key, $taken_keys)) return $key; + + $key = sprintf("%s (%s-%s)", $label, AbstractSerializer::getCSVLabel($name), $id); + $base = $key; + $i = 2; + while (array_key_exists($key, $taken_keys)) { + $key = sprintf("%s-%s", $base, $i++); + } + return $key; + } + /** * @param null $expand * @param array $fields @@ -93,7 +118,13 @@ public function serialize($expand = null, array $fields = array(), array $relati if (!self::isAllowedExtraQuestion($allowed_sponsor_extra_questions, $question->getId())) continue; - $label = AbstractSerializer::getCSVLabel($question->getLabel()); + $label = self::getUniqueColumnKey + ( + $values + $sponsor_questions_values, + AbstractSerializer::getCSVLabel($question->getLabel()), + $question->getName(), + $question->getId() + ); Log::debug ( sprintf @@ -130,9 +161,15 @@ public function serialize($expand = null, array $fields = array(), array $relati foreach ($params['ticket_questions'] as $question) { if (!self::isAllowedExtraQuestion($allowed_attendee_extra_questions, $question->getId())) continue; - $label = AbstractSerializer::getCSVLabel($question->getLabel()); - if (!$question instanceof SummitOrderExtraQuestionType) continue; + + $label = self::getUniqueColumnKey + ( + $values + $sponsor_questions_values + $ticket_questions_values, + AbstractSerializer::getCSVLabel($question->getLabel()), + $question->getName(), + $question->getId() + ); $ticket_questions_values[$label] = ''; if (!is_null($ticket_owner)) { diff --git a/tests/SponsorBadgeScanCSVSerializerTest.php b/tests/SponsorBadgeScanCSVSerializerTest.php new file mode 100644 index 000000000..e9c348b18 --- /dev/null +++ b/tests/SponsorBadgeScanCSVSerializerTest.php @@ -0,0 +1,48 @@ +setAccessible(true); + return $method->invoke(null, $taken, $label, $name, $id); + } + + public function testNoCollisionKeepsLabel(): void + { + $this->assertSame('company size', $this->uniqueKey(['notes' => 'x'], 'company size', 'COMPANY_SIZE', 1)); + } + + public function testCollisionWithFixedColumnUsesQuestionName(): void + { + $this->assertSame('notes (notes_q)', $this->uniqueKey(['notes' => 'scan notes'], 'notes', 'NOTES_Q', 10)); + } + + public function testCollisionWithSameLabelAndNameUsesQuestionId(): void + { + $taken = ['notes' => 'a', 'notes (notes_q)' => 'b']; + $this->assertSame('notes (notes_q-11)', $this->uniqueKey($taken, 'notes', 'notes_q', 11)); + } + + public function testCollisionWithIdKeyUsesCounter(): void + { + $taken = ['notes' => 'a', 'notes (notes_q)' => 'b', 'notes (notes_q-11)' => 'c']; + $this->assertSame('notes (notes_q-11)-2', $this->uniqueKey($taken, 'notes', 'notes_q', 11)); + } +}