Skip to content

Commit

Permalink
Fix survey mapping to include all currently used question types
Browse files Browse the repository at this point in the history
  • Loading branch information
sten committed Sep 5, 2022
1 parent 01b99fa commit b36a50f
Show file tree
Hide file tree
Showing 19 changed files with 321 additions and 105 deletions.
2 changes: 1 addition & 1 deletion database/migrations/create_surveyhero_tables.php.stub
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ class CreateSurveyheroTables extends Migration
$table->bigInteger('surveyhero_answer_id')->nullable();
$table->string('converted_string_value')->nullable();
$table->integer('converted_int_value')->nullable();
$table->json('label');
$table->json('label')->nullable();
$table->timestamps();
});

Expand Down
16 changes: 7 additions & 9 deletions src/Commands/SurveyheroMapperCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,16 +46,18 @@ public function handle(): int
$this->comment("Mapping for survey '$survey->name' completed!");
}

$myfile = fopen('mapping.txt', 'w') or exit('Unable to open file!');
fwrite($myfile, $this->var_export_short($mapping, true));
$fileName = 'surveyhero_mapping.php';
$myfile = fopen($fileName, 'w') or exit('Unable to open file!');

fwrite($myfile, "<?php \n\n" . $this->var_export_short($mapping) . "; \n");
fclose($myfile);

$this->comment('Mapping complete! [mapping.txt]');
$this->comment("Mapping complete! [$fileName]");

return self::SUCCESS;
}

private function var_export_short($data, $return = true)
private function var_export_short($data): string
{
$dump = var_export($data, true);

Expand All @@ -70,10 +72,6 @@ private function var_export_short($data, $return = true)
$dump = preg_replace('#\)$#', ']', $dump);
}

if ($return === true) {
return $dump;
} else {
echo $dump;
}
return $dump;
}
}
15 changes: 15 additions & 0 deletions src/Exceptions/QuestionMapperNotImplementedException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

namespace Statikbe\Surveyhero\Exceptions;

class QuestionMapperNotImplementedException extends \Exception {
public string $questionType;

public static function create(string $questionType): self
{
$ex = new self("There is no mapper implementation for question type: $questionType");
$ex->questionType = $questionType;

return $ex;
}
}
6 changes: 3 additions & 3 deletions src/Exceptions/QuestionNotImportedException.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@

class QuestionNotImportedException extends \Exception
{
public int $answerId;
public int $questionId;

public static function create(int $answerId, string $message): self
public static function create(int $questionId, string $message): self
{
$ex = new self($message);
$ex->answerId = $answerId;
$ex->questionId = $questionId;

return $ex;
}
Expand Down
3 changes: 3 additions & 0 deletions src/Models/SurveyAnswer.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ class SurveyAnswer extends Model
use HasFactory;
use HasTranslations;

const CONVERTED_TYPE_INT = 'int';
const CONVERTED_TYPE_STRING = 'string';

protected $translatable = ['label'];

protected $guarded = [];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Statikbe\Surveyhero\Exceptions\AnswerNotMappedException;
use Statikbe\Surveyhero\Models\Survey;
use Statikbe\Surveyhero\Models\SurveyAnswer;
use Statikbe\Surveyhero\Models\SurveyQuestion;
use Statikbe\Surveyhero\Services\SurveyMappingService;

Expand Down Expand Up @@ -52,17 +53,17 @@ protected function setChoiceAndConvertToDataType(mixed $mappedChoice,
{
//if the choice is not mapped try to set the label as string:
if (! $mappedChoice) {
if ($dataType === 'string') {
if ($dataType === SurveyAnswer::CONVERTED_TYPE_STRING) {
$responseData['converted_string_value'] = $surveyheroChoice->label;
} else {
throw AnswerNotMappedException::create($surveyheroChoice->choice_id, "The choice mapping could not be made for choice ID: $surveyheroChoice->choice_id");
}
} else {
switch ($dataType) {
case 'int':
case SurveyAnswer::CONVERTED_TYPE_INT:
$responseData['converted_int_value'] = $mappedChoice;
break;
case 'string':
case SurveyAnswer::CONVERTED_TYPE_STRING:
$responseData['converted_string_value'] = $mappedChoice;
break;
}
Expand Down
23 changes: 23 additions & 0 deletions src/Services/Factories/QuestionMapper/AbstractQuestionMapper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

namespace Statikbe\Surveyhero\Services\Factories\QuestionMapper;

use Statikbe\Surveyhero\Models\SurveyAnswer;

abstract class AbstractQuestionMapper implements QuestionMapper {
/**
* @param int $questionId
* @param string $questionType
* @param string $mappedDataType
* @param int|string $questionFieldSuffix
* @return array{'question_id': int, 'type': string, 'field': string, 'mapped_data_type': string }
*/
public function createQuestionMap(int $questionId, string $questionType, string $mappedDataType, int|string $questionFieldSuffix): array {
return [
'question_id' => $questionId,
'type' => $questionType,
'field' => 'question_'.$questionFieldSuffix,
'mapped_data_type' => $mappedDataType,
];
}
}
22 changes: 22 additions & 0 deletions src/Services/Factories/QuestionMapper/ChoiceListQuestionMapper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

namespace Statikbe\Surveyhero\Services\Factories\QuestionMapper;

use Statikbe\Surveyhero\Models\SurveyAnswer;
use Statikbe\Surveyhero\Services\Factories\QuestionAndAnswerCreator\ChoiceListQuestionAndAnswerCreator;

class ChoiceListQuestionMapper extends AbstractQuestionMapper {
const TYPE = 'choice_list';

public function mapQuestion(\stdClass $question, int $questionCounter): array {
$questionData = $this->createQuestionMap($question->element_id,
$question->question->type,
SurveyAnswer::CONVERTED_TYPE_INT,
$questionCounter);

foreach ($question->question->choice_list->choices as $choiceKey => $choice) {
$questionData['answer_mapping'][$choice->choice_id] = $choiceKey + 1;
}
return $questionData;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

namespace Statikbe\Surveyhero\Services\Factories\QuestionMapper;

use Statikbe\Surveyhero\Models\SurveyAnswer;

class ChoiceTableQuestionMapper extends AbstractQuestionMapper {
const TYPE = 'choice_table';

public function mapQuestion(\stdClass $question, int $questionCounter): array {
$mappedQuestions = [];
$subquestionIndex = 1;
// make answer mapping which is the same for each question:
$answerMapping = [];
$choiceCounter = 1;
foreach($question->question->choice_table->choices as $questionChoice){
$answerMapping[$questionChoice->choice_id] = $choiceCounter;
$choiceCounter ++;
}

//create subquestions:
foreach ($question->question->choice_table->rows as $rowQuestion){
$questionData = $this->createQuestionMap($rowQuestion->row_id,
$question->question->type,
SurveyAnswer::CONVERTED_TYPE_INT,
"{$questionCounter}_{$subquestionIndex}");

$questionData['answer_mapping'] = $answerMapping;
$mappedQuestions[] = $questionData;
}

return $mappedQuestions;
}
}
18 changes: 18 additions & 0 deletions src/Services/Factories/QuestionMapper/InputQuestionMapper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

namespace Statikbe\Surveyhero\Services\Factories\QuestionMapper;

use Statikbe\Surveyhero\Models\SurveyAnswer;

class InputQuestionMapper extends AbstractQuestionMapper {
const TYPE = 'input';

public function mapQuestion(\stdClass $question, int $questionCounter): array {
$questionData = $this->createQuestionMap($question->element_id,
$question->question->type,
SurveyAnswer::CONVERTED_TYPE_STRING,
$questionCounter);

return $questionData;
}
}
7 changes: 7 additions & 0 deletions src/Services/Factories/QuestionMapper/QuestionMapper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

namespace Statikbe\Surveyhero\Services\Factories\QuestionMapper;

interface QuestionMapper {
public function mapQuestion(\stdClass $question, int $questionCounter): array ;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

namespace Statikbe\Surveyhero\Services\Factories\QuestionMapper;

use Statikbe\Surveyhero\Models\SurveyAnswer;

class RatingScaleQuestionMapper extends AbstractQuestionMapper {
const TYPE = 'rating_scale';

public function mapQuestion(\stdClass $question, int $questionCounter): array {
$questionData = $this->createQuestionMap($question->element_id,
$question->question->type,
SurveyAnswer::CONVERTED_TYPE_STRING,
$questionCounter);

if ($question->question->rating_scale->style == 'numerical_scale') {
$questionData['mapped_data_type'] = SurveyAnswer::CONVERTED_TYPE_INT;
}

return $questionData;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

namespace Statikbe\Surveyhero\Services\Factories\ResponseCreator;

use Statikbe\Surveyhero\Exceptions\AnswerNotImportedException;
use Statikbe\Surveyhero\Exceptions\AnswerNotMappedException;
use Statikbe\Surveyhero\Exceptions\QuestionNotImportedException;
use Statikbe\Surveyhero\Models\SurveyAnswer;
use Statikbe\Surveyhero\Models\SurveyQuestion;
use Statikbe\Surveyhero\Models\SurveyQuestionResponse;
use Statikbe\Surveyhero\Models\SurveyResponse;
Expand Down Expand Up @@ -34,27 +36,43 @@ protected function findExistingQuestionResponse(string|int $surveyheroQuestionId
}

/**
* @param \stdClass $surveyheroQuestionResponse
* @param SurveyResponse $response
* @param string $field
* @return array{ 'survey_question_id': int, 'field': string, 'survey_response_id': int }
*
* @throws \Statikbe\Surveyhero\Exceptions\QuestionNotImportedException
* @param string $surveyheroQuestionId
* @return SurveyQuestion
* @throws QuestionNotImportedException
*/
protected function createSurveyQuestionResponseData(\stdClass $surveyheroQuestionResponse,
SurveyResponse $response,
string $field): array
{
$surveyQuestion = SurveyQuestion::where('surveyhero_question_id', $surveyheroQuestionResponse->element_id)->first();
protected function findSurveyQuestion(string $surveyheroQuestionId): SurveyQuestion {
$surveyQuestion = SurveyQuestion::where('surveyhero_question_id', $surveyheroQuestionId)->first();
if(!$surveyQuestion){
throw QuestionNotImportedException::create($surveyheroQuestionId, 'The question is not imported');
}
else return $surveyQuestion;
}

if (! $surveyQuestion) {
throw QuestionNotImportedException::create($surveyheroQuestionResponse->element_id, "Make sure to import survey question with Surveyhero ID $surveyheroQuestionResponse->element_id in the survey_questions table");
protected function findSurveyAnswer(SurveyQuestion $question, string $surveyheroAnswerId): SurveyAnswer {
$surveyAnswer = SurveyAnswer::where('survey_question_id', $question->id)
->where('surveyhero_answer_id', $surveyheroAnswerId)
->first();

if (! $surveyAnswer) {
throw AnswerNotImportedException::create($surveyheroAnswerId, "Make sure to import survey answer with Surveyhero ID $surveyheroAnswerId in the survey_answers table");
}
else return $surveyAnswer;
}

/**
* @param SurveyQuestion $question
* @param SurveyResponse $response
* @param SurveyAnswer|null $answer
* @return array{ 'survey_question_id': int, 'survey_response_id': int }
*/
protected function createSurveyQuestionResponseData(SurveyQuestion $question,
SurveyResponse $response,
?SurveyAnswer $answer): array
{
return [
'survey_question_id' => $surveyQuestion->id,
'field' => $field,
'survey_question_id' => $question->id,
'survey_response_id' => $response->id,
'survey_answer_id' => $answer ? $answer->id : null,
];
}

Expand All @@ -71,31 +89,67 @@ protected function getChoiceMapping(string|int $choiceId, array $questionMapping
* @param mixed $mappedChoice
* @param string $dataType
* @param array $responseData
* @param \stdClass $surveyheroChoice
* @param \stdClass|null $surveyheroChoice
*
* @throws AnswerNotMappedException
*/
protected function setChoiceAndConvertToDataType(mixed $mappedChoice,
string $dataType,
array &$responseData,
\stdClass $surveyheroChoice): void
?\stdClass $surveyheroChoice): void
{
//if the choice is not mapped try to set the label as string:
if (! $mappedChoice) {
if ($dataType === 'string') {
if (! $mappedChoice && $surveyheroChoice) {
if ($dataType === SurveyAnswer::CONVERTED_TYPE_STRING) {
$responseData['converted_string_value'] = $surveyheroChoice->label;
} else {
throw AnswerNotMappedException::create($surveyheroChoice->choice_id, "The choice mapping could not be made for choice ID: $surveyheroChoice->choice_id");
}
} else {
switch ($dataType) {
case 'int':
case SurveyAnswer::CONVERTED_TYPE_INT:
$responseData['converted_int_value'] = $mappedChoice;
break;
case 'string':
case SurveyAnswer::CONVERTED_TYPE_STRING:
$responseData['converted_string_value'] = $mappedChoice;
break;
}
}
}

protected function fetchOrCreateInputAnswer(SurveyQuestion $surveyQuestion, string $answerDataType, mixed $inputAnswer): SurveyAnswer {
//fetch or create answer:
$answerData = [];
$this->setChoiceAndConvertToDataType($this->transformInputToDataType($inputAnswer, $answerDataType),
$answerDataType,
$answerData,
null);
$surveyAnswerQuery = SurveyAnswer::where('survey_question_id', $surveyQuestion->id);
if(isset($answerData['converted_int_value'])){
$surveyAnswerQuery->where('converted_int_value', $answerData['converted_int_value']);
}
else if(isset($answerData['converted_string_value'])){
$surveyAnswerQuery->where('converted_string_value', $answerData['converted_string_value']);
}
$surveyAnswer = $surveyAnswerQuery->first();

if (! $surveyAnswer) {
$answerData['survey_question_id'] = $surveyQuestion->id;
$answerData['surveyhero_answer_id'] = null;
$surveyAnswer = SurveyAnswer::create($answerData);
}

return $surveyAnswer;
}

protected function transformInputToDataType(mixed $input, string $dataType){
switch($dataType){
case SurveyAnswer::CONVERTED_TYPE_INT:
return intval($input);
case SurveyAnswer::CONVERTED_TYPE_STRING:
return strval($input);
default:
return $input;
}
}
}
Loading

0 comments on commit b36a50f

Please sign in to comment.