Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Review: 1597-review-remaining-element-level-data-completeness-alerts-for-correctness #1636

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions app/Console/Commands/RefreshActivityElementCompleteness.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Exception;
use Illuminate\Console\Command;
use Illuminate\Contracts\Container\BindingResolutionException;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\DB;

/**
Expand Down Expand Up @@ -45,22 +46,29 @@ public function handle(): void
foreach ($activities as $activity) {
$this->info("Started for activity: $activity->id");

$activityElementNames = $activity->getAttributes();
$activityElementNames = getActivityAttributes();
$elementStatus = [];

foreach ($activityElementNames as $element => $value) {
foreach ($activityElementNames as $element) {
$methodName = dashesToCamelCase('is_' . $element . '_element_completed');

if (method_exists($elementCompleteService, $methodName)) {
$elementStatus[$element] = $elementCompleteService->$methodName($activity);
if ($element === 'reporting_org') {
$organization = $activity->organization;
$elementStatus[$element] = Arr::get($organization, 'element_status.reporting_org', false);
} else {
$elementStatus[$element] = $elementCompleteService->$methodName($activity);
}
}
}

$elementStatus['result'] = $elementCompleteService->isResultElementCompleted($activity);
$elementStatus['transactions'] = $elementCompleteService->isTransactionsElementCompleted($activity);

$complete_percentage = $elementCompleteService->calculateCompletePercentage($elementStatus);

$activity->timestamps = false;
$activity->updateQuietly(['element_status' => $elementStatus]);
$activity->updateQuietly(['element_status' => $elementStatus, 'complete_percentage' => $complete_percentage]);

$this->info("Completed for activity: $activity->id");
$this->info('---------------------------------------');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use App\CsvImporter\Entities\Activity\Components\Factory\Validation;
use App\Http\Requests\Activity\DefaultAidType\DefaultAidTypeRequest;
use App\IATI\Traits\DataSanitizeTrait;
use Illuminate\Support\Arr;

/**
* Class DefaultAidType.
Expand Down Expand Up @@ -137,8 +138,8 @@ protected function setDefaultAidTypeCode($key, $value, $index): void
}

if ($key === $this->_csvHeaders[1]) {
$defaultAidTypeVocabulary = $this->data['default_aid_type'][$index]['default_aid_type_vocabulary'] ?? '';
$defaultAidTypeVocabulary = empty($defaultAidTypeVocabulary) ?: (int) $defaultAidTypeVocabulary;
$accessKey = "default_aid_type.$index.default_aid_type_vocabulary";
$defaultAidTypeVocabulary = Arr::get($this->data, $accessKey, '1');
$value = is_null($value) ? '' : trim($value);

switch ($defaultAidTypeVocabulary) {
Expand Down
95 changes: 63 additions & 32 deletions app/CsvImporter/Entities/Activity/Components/Elements/Sector.php
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ public function prepare($fields): void
* @param $index
*
* @return void
* @throws \JsonException
*/
public function map($key, $value, $index): void
{
Expand Down Expand Up @@ -161,45 +162,48 @@ protected function setSectorVocabulary($key, $value, $index): void
/**
* Set sector code for Sector.
*
* @param $key
* @param $value
* @param $index
* This method handles setting the sector code based on the sector vocabulary.
*
* Extra condition:
* - Adds or unsets the 'text' key based on allowed sector vocabularies.
* - Allowed vocabularies: null, '', '3', '4', '5', '6', '9', '10', '11', '12', '98', '99'.
* - If the sector vocabulary is one of the allowed values, it makes sure 'text' key exists.
* - If the sector vocabulary is not allowed, it unsets the 'text' key.
*
* Based on the sector vocabulary, it performs different actions:
* - '1' -> Calls handleSectorVocabularyOne() to process value.
* - '2' -> Calls setSectorCategoryCode() to process value.
* - '7' -> Calls setSectorSdgGoal() to process value.
* - '8' -> Calls setSectorSdgTarget() to process value.
* - default -> Calls setSectorText() to process value.
*
* @param mixed $key The key for the sector field.
* @param mixed $value The value to be processed for the sector.
* @param int $index The index of the sector.
*
* @return void
* @throws \JsonException
*/
protected function setSectorCode($key, $value, $index): void
{
if (!isset($this->data['sector'][$index]['text'])) {
$this->data['sector'][$index]['text'] = '';
$accessKey = "sector.$index.sector_vocabulary";
$allowedVocabularies = [null, '', '3', '4', '5', '6', '9', '10', '11', '12', '98', '99'];
$sectorVocabulary = Arr::get($this->data, $accessKey);

if (!in_array($sectorVocabulary, $allowedVocabularies, true)) {
unset($this->data['sector'][$index]['text']);
} else {
$this->data['sector'][$index]['text'] = $this->data['sector'][$index]['text'] ?? '';
}

if ($key === $this->_csvHeaders[1]) {
$sectorVocabulary = Arr::get($this->data['sector'], $index . '.sector_vocabulary', null);

if ($sectorVocabulary === '1') {
$value = $value ? trim($value) : $value;
$this->codes[] = $value;
$validSectorCode = $this->loadCodeList('SectorCode');

if ($value) {
foreach ($validSectorCode as $code => $name) {
if (strcasecmp($value, $name) === 0) {
$value = (string) $code;
break;
}
}
}

$this->data['sector'][$index]['code'] = $value;
} elseif ($sectorVocabulary === '2') {
$this->setSectorCategoryCode($value, $index);
} elseif ($sectorVocabulary === '7') {
$this->setSectorSdgGoal($value, $index);
} elseif ($sectorVocabulary === '8') {
$this->setSectorSdgTarget($value, $index);
} else {
$this->setSectorText($value, $index);
}
match ($sectorVocabulary) {
'1' => $this->handleSectorVocabularyOne($value, $index),
'2' => $this->setSectorCategoryCode($value, $index),
'7' => $this->setSectorSdgGoal($value, $index),
'8' => $this->setSectorSdgTarget($value, $index),
default => $this->setSectorText($value, $index),
};
}
}

Expand All @@ -219,6 +223,33 @@ protected function setVocabularyUri($key, $value, $index): void
}
}

/**
* Handle for sector vocab 1.
*
* @param $value
* @param $index
*
* @return void
* @throws \JsonException
*/
protected function handleSectorVocabularyOne($value, $index): void
{
$value = $value ? trim($value) : $value;
$this->codes[] = $value;
$validSectorCode = $this->loadCodeList('SectorCode');

if ($value) {
foreach ($validSectorCode as $code => $name) {
if (strcasecmp($value, $name) === 0) {
$value = (string) $code;
break;
}
}
}

$this->data['sector'][$index]['code'] = $value;
}

/**
* Set sector category code for Sector.
*
Expand Down Expand Up @@ -301,7 +332,7 @@ protected function setSectorSdgTarget($value, $index): void
*/
protected function setSectorText($value, $index): void
{
($value) ?: $value = '';
$value = $value ?: '';
$this->codes[] = $value;
$this->data['sector'][$index]['text'] = $value;
}
Expand Down
130 changes: 102 additions & 28 deletions app/CsvImporter/Entities/Activity/Components/Elements/Tag.php
Original file line number Diff line number Diff line change
Expand Up @@ -135,46 +135,120 @@ protected function setTagVocabulary($key, $value, $index): void
* @return void
*/
protected function setTagCode($key, $value, $index): void
{
if ($key === $this->_csvHeaders[1]) {
if (!isset($this->data['tag'][$index]['tag_text'])) {
$this->data['tag'][$index]['tag_text'] = '';
}

$tagVocabulary = $this->data['tag'][$index]['tag_vocabulary'] ?? '';
$value = (!$value) ? '' : trim((string) $value);

switch ($tagVocabulary) {
case '2':
$this->handleGoalsTag($index, $value);
break;

case '3':
$this->handleTargetsTag($index, $value);
break;

case '1':
case '4':
$this->handleTextTag($index, $value, $tagVocabulary);
break;

default:
$this->handleDefaultTag($index, $value, $tagVocabulary);
break;
}
}
}

/**
* Initialize the tag structure if not set.
*/
private function initializeTag($index): void
{
if (!isset($this->data['tag'][$index]['tag_text'])) {
$this->data['tag'][$index]['tag_text'] = '';
}
}

if ($key === $this->_csvHeaders[1]) {
$tagVocabulary = $this->data['tag'][$index]['tag_vocabulary'] ?? '';
$value = (!$value) ? '' : trim((string) $value);
/**
* Sanitize value by trimming and converting to string.
*/
private function sanitizeValue($value): string
{
return (!$value) ? '' : trim((string) $value);
}

/**
* Handle case for '2' (UNSDG-Goals).
*/
private function handleGoalsTag($index, $value): void
{
$value = $this->getMappedCode('UNSDG-Goals', $value);
$this->data['tag'][$index]['goals_tag_code'] = $value;
$this->updateTagFields($index, ['tag_vocabulary', 'goals_tag_code', 'narrative']);
}

if ($tagVocabulary === '2') {
$validTagCode = $this->loadCodeList('UNSDG-Goals');
/**
* Handle case for '3' (UNSDG-Targets).
*/
private function handleTargetsTag($index, $value): void
{
$value = $this->getMappedCode('UNSDG-Targets', $value, true);
$this->data['tag'][$index]['targets_tag_code'] = $value;
$this->updateTagFields($index, ['tag_vocabulary', 'targets_tag_code', 'narrative']);
}

if ($value) {
foreach ($validTagCode as $code => $name) {
if (strcasecmp($value, $name) === 0) {
$value = (string) $code;
break;
}
}
}
/**
* Handle cases '1' and '4' (tag_text).
*/
private function handleTextTag($index, $value, $tagVocabulary): void
{
$this->data['tag'][$index]['tag_text'] = $value;
$this->data['tag'][$index]['tag_vocabulary'] = $tagVocabulary;
$this->updateTagFields($index, ['tag_vocabulary', 'tag_text', 'narrative']);
}

/**
* Handle the default case.
*/
private function handleDefaultTag($index, $value, $tagVocabulary): void
{
$this->data['tag'][$index]['tag_text'] = $value;
$this->data['tag'][$index]['tag_vocabulary'] = $tagVocabulary;
$this->updateTagFields($index, ['tag_vocabulary', 'tag_text', 'narrative', 'vocabulary_uri']);
}

$this->data['tag'][$index]['goals_tag_code'] = $value;
} elseif ($tagVocabulary === '3') {
$validTagCode = $this->loadCodeList('UNSDG-Targets');
/**
* Retrieve the mapped code from a code list.
*/
private function getMappedCode(string $listName, string $value, bool $allowFloat = false): float|int|string
{
if (!$value) {
return $value;
}

if (!is_float($value)) {
foreach ($validTagCode as $code => $name) {
if (strcasecmp($value, $name) === 0) {
$value = is_float($code) ? (float) $code : $code;
break;
}
}
}
$validTagCode = $this->loadCodeList($listName);

$this->data['tag'][$index]['targets_tag_code'] = $value;
} else {
$this->data['tag'][$index]['tag_text'] = $value;
$this->data['tag'][$index]['tag_vocabulary'] = $tagVocabulary;
foreach ($validTagCode as $code => $name) {
if (strcasecmp($value, $name) === 0) {
return $allowFloat && is_float($code) ? (float) $code : $code;
}
}

return $value;
}

/**
* Update tag fields while keeping only necessary attributes.
*/
private function updateTagFields($index, array $fields): void
{
$this->data['tag'][$index] = Arr::only($this->data['tag'][$index], $fields);
}

/**
Expand Down
46 changes: 46 additions & 0 deletions app/Helpers/general.php
Original file line number Diff line number Diff line change
Expand Up @@ -1549,3 +1549,49 @@ function regroupResponseForAllActivity(array $response, array $uniqueIdentifiers

return $groupedResponses;
}

/**
* Returns array of activity element names in snake case.
* Making this instead of using $activity->getAttributes() method that laravel has because:
* There were case where I was not getting attributes reliably.
* Need this function when using ElementCompleteService::refreshElementStatus().
*
* @return string[]
*/
function getActivityAttributes(): array
{
return [
'iati_identifier',
'other_identifier',
'title',
'description',
'activity_status',
'activity_date',
'contact_info',
'activity_scope',
'participating_org',
'recipient_country',
'recipient_region',
'location',
'sector',
'country_budget_items',
'humanitarian_scope',
'policy_marker',
'collaboration_type',
'default_flow_type',
'default_finance_type',
'default_aid_type',
'default_tied_status',
'budget',
'planned_disbursement',
'capital_spend',
'document_link',
'related_activity',
'legacy_data',
'conditions',
'tag',
'reporting_org',
'transactions',
'result',
];
}
Loading