Skip to content

Commit

Permalink
Merge pull request #1740 from younginnovations/1567-check-expected-be…
Browse files Browse the repository at this point in the history
…haviour-of-publishing-progress-box-when-exiting-to-view-data-quality-issues

Reviewed: 1567-check-expected-behaviour-of-publishing-progress-box-when-exiting-to-view-data-quality-issues
  • Loading branch information
PG-Momik authored Jan 28, 2025
2 parents 987d171 + 1f57e16 commit a17d277
Show file tree
Hide file tree
Showing 31 changed files with 2,317 additions and 420 deletions.
129 changes: 129 additions & 0 deletions app/Helpers/BulkPublishCacheHelper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
<?php

declare(strict_types=1);

namespace App\Helpers;

use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Cache;

class BulkPublishCacheHelper
{
/**
* Default cache structure when there is no cache found for the organization.
*
* @var array
*/
private const EMPTY_CACHE = [
'status' => false,
'activity_ids' => [],
];

/**
* The cache key format used to store bulk publish data for an organization.
*
* @var string
*/
private const CACHE_KEY = 'ongoing_bulk_publish_%s';

/**
* Get the cache key for the organization.
*
* @param int $orgId
* @return string
*/
private static function getCacheKey(int $orgId): string
{
return sprintf(self::CACHE_KEY, (string) $orgId);
}

/**
* Retrieve the bulk publish cache for the given organization.
*
* @param int $orgId
* @return array The bulk publish cache or empty cache if none found
*/
public static function getOrganisationBulkPublishCache(int $orgId): array
{
return Cache::get(self::getCacheKey($orgId), self::EMPTY_CACHE);
}

/**
* Check if there is an ongoing bulk publish for a given organization.
*
* @param int $orgId
* @return bool True if there is an ongoing bulk publish, false otherwise
*/
public static function hasOngoingBulkPublish(int $orgId): bool
{
return Arr::get(self::getOrganisationBulkPublishCache($orgId), 'status', false);
}

public static function getActivityIdsInCache(int $orgId): array
{
return Arr::get(self::getOrganisationBulkPublishCache($orgId), 'activity_ids', []);
}

public static function activitiesHaveChanged(int $orgId): bool
{
return count(self::getActivityIdsInCache($orgId)) > 0;
}

/**
* Set the initial bulk publish cache for a given organization.
*
* @param int $orgId
* @return void
*/
public static function setInitialBulkPublishCache(int $orgId): void
{
if (!self::hasOngoingBulkPublish($orgId)) {
$cacheValue = self::EMPTY_CACHE;
$cacheValue['status'] = true;

self::updateCache($orgId, $cacheValue);
}
}

/**
* Append an activity ID in the bulk publish cache for a given organization.
*
* @param int $orgId
* @param int $activityId
* @return void
*/
public static function appendActivityIdInBulkPublishCache(int $orgId, int $activityId): void
{
$cacheValue = self::getOrganisationBulkPublishCache($orgId);

if (!in_array($activityId, $cacheValue['activity_ids'], true)) {
$cacheValue['activity_ids'][] = $activityId;

self::updateCache($orgId, $cacheValue);
}
}

/**
* Clear the cache for a given organization.
* This removes the bulk publish data from the cache.
*
* @param int $orgId
* @return void
*/
public static function clearBulkPublishCache(int $orgId): void
{
Cache::forget(self::getCacheKey($orgId));
}

/**
* Helper method to update the cache for the given organization.
*
* @param int $orgId
* @param array $cacheValue
* @return void
*/
private static function updateCache(int $orgId, array $cacheValue): void
{
Cache::put(self::getCacheKey($orgId), $cacheValue);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -198,16 +198,10 @@ public function importValidatedActivities(Request $request): mixed
$orgId = Auth::user()->organization_id;
$filetype = Session::get('import_filetype') ?? ImportCacheHelper::getSessionConsistentFiletype($orgId);

logger('ImportCacheHelper::organisationHasCompletedValidatingData($orgId)');
logger(ImportCacheHelper::organisationHasCompletedValidatingData($orgId));
logger('ImportCacheHelper::getImportStep($orgId)');
logger(ImportCacheHelper::getImportStep($orgId));

if (!ImportCacheHelper::organisationHasCompletedValidatingData($orgId)) {
return response()->json(['success' => false, 'message' => 'No data to import.', 'type' => $filetype]);
}

logger('thichna paiyo00');
ImportCacheHelper::setImportStepToImported($orgId);

if ($activities) {
Expand Down
85 changes: 75 additions & 10 deletions app/Http/Controllers/Admin/Workflow/BulkPublishingController.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use App\Constants\Enums;
use App\Exceptions\MaxBatchSizeExceededException;
use App\Exceptions\MaxMergeSizeExceededException;
use App\Helpers\BulkPublishCacheHelper;
use App\Http\Controllers\Controller;
use App\IATI\Elements\Xml\XmlGenerator;
use App\IATI\Services\Activity\ActivityPublishedService;
Expand Down Expand Up @@ -56,10 +57,11 @@ class BulkPublishingController extends Controller
/**
* BulkPublishingController Constructor.
*
* @param BulkPublishingService $bulkPublishingService
* @param ActivityService $activityService
* @param ActivityWorkflowService $activityWorkflowService
* @param BulkPublishingStatusService $publishingStatusService
* @param BulkPublishingService $bulkPublishingService
* @param ActivityService $activityService
* @param ActivityWorkflowService $activityWorkflowService
* @param BulkPublishingStatusService $publishingStatusService
* @param ActivityPublishedService $activityPublishedService
*/
public function __construct(
BulkPublishingService $bulkPublishingService,
Expand Down Expand Up @@ -263,20 +265,33 @@ public function startBulkPublish(Request $request): JsonResponse
}

if ($this->publishingStatusService->ongoingBulkPublishing($organization->id)) {
$pubishingStatus = $this->bulkPublishingService->getOrganisationBulkPublishingStatus();
$publishingStatus = $this->bulkPublishingService->getOrganisationBulkPublishingStatus();

return response()->json([
'success' => false,
'message' => 'Another bulk publishing is already in progress.',
'data' => $pubishingStatus['publishingData'],
'in_progress' => $pubishingStatus['inProgress'],
'data' => $publishingStatus['publishingData'],
'in_progress' => $publishingStatus['inProgress'],
]);
}

$activityIds = json_decode($request->get('activities'), false, 512, JSON_THROW_ON_ERROR);

if (!empty($activityIds)) {
$activities = $this->activityService->getActivitiesHavingIds($activityIds);
$filteredActivityIds = $this->bulkPublishingService->getPublishableActivityIds($activityIds);

if (empty($filteredActivityIds)) {
return response()->json(
[
'success' => false,
'status' => 'error',
'message' => 'All of the selected activities have critical error.',
'data' => ['activity_ids' => $activityIds],
]
);
}

$activities = $this->activityService->getActivitiesHavingIds($filteredActivityIds);

if (!count($activities)) {
return response()->json(['success' => false, 'message' => 'No activities selected.']);
Expand Down Expand Up @@ -311,7 +326,6 @@ public function startBulkPublish(Request $request): JsonResponse
return response()->json(['success' => false, 'message' => 'No activities selected.']);
} catch (Exception $e) {
DB::rollBack();
logger()->error($e->getMessage());
logger()->error($e);

return response()->json(['success' => false, 'message' => 'Bulk publishing failed.']);
Expand All @@ -337,6 +351,10 @@ public function getBulkPublishStatus(): JsonResponse
if ($publishStatus && count($publishStatus)) {
$response = $this->bulkPublishingService->getPublishingResponse($publishStatus);

if ($this->bulkPublishCompleted($response)) {
BulkPublishCacheHelper::clearBulkPublishCache($organizationId);
}

return response()->json(
[
'success' => true,
Expand Down Expand Up @@ -371,9 +389,12 @@ public function cancelBulkPublishing(): JsonResponse
{
try {
DB::beginTransaction();
$deletedIds = $this->bulkPublishingService->stopBulkPublishing(auth()->user()->organization->id);
$orgId = auth()->user()->organization_id;
$deletedIds = $this->bulkPublishingService->stopBulkPublishing($orgId);
$numberOfDeletedRows = count($deletedIds);

BulkPublishCacheHelper::clearBulkPublishCache($orgId);

if ($deletedIds) {
DB::commit();

Expand All @@ -389,6 +410,7 @@ public function cancelBulkPublishing(): JsonResponse
return response()->json(['success' => true, 'message' => 'No bulk publish were cancelled.']);
} catch (Exception $e) {
DB::rollBack();
BulkPublishCacheHelper::clearBulkPublishCache(auth()->user()->organization_id);
logger()->error($e->getMessage());
logger()->error($e);

Expand Down Expand Up @@ -505,6 +527,10 @@ public function getValidationStatus(Request $request): JsonResponse
$filteredActivityIds = $this->filterOutPublishedStateActivityIds($activityIds, $activities->toArray());
$response = $this->bulkPublishingService->getActivityValidationStatus($filteredActivityIds);

if ($this->validationCompleted($response)) {
BulkPublishCacheHelper::setInitialBulkPublishCache(auth()->user()->organization_id);
}

$hasFailedStatus = $response['failed_count'] > 0;

return response()->json(['success' => !$hasFailedStatus, 'data' => $response]);
Expand Down Expand Up @@ -559,6 +585,7 @@ public function deleteValidationStatus(): JsonResponse
$this->bulkPublishingService->deleteValidationResponses();
Cache::put('activity-validation-delete', true);
Cache::forget('activity-validation-' . auth()->user()->id);
BulkPublishCacheHelper::clearBulkPublishCache(auth()->user()->organization_id);

return response()->json(['success' => true, 'message' => 'Successfully Deleted.']);
} catch (Exception|\Throwable $exception) {
Expand All @@ -570,6 +597,34 @@ public function deleteValidationStatus(): JsonResponse
}
}

/**
* @return JsonResponse
*/
public function detectChange(): JsonResponse
{
try {
$orgId = auth()->user()->organization_id;
$hasChanged = BulkPublishCacheHelper::activitiesHaveChanged($orgId);
$affectedActivities = BulkPublishCacheHelper::getActivityIdsInCache($orgId);

return response()->json([
'success' => true,
'message' => $hasChanged ? 'Change detected.' : 'No change detected.',
'data' => [
'has_changed' => $hasChanged,
'affected_activities' => $affectedActivities,
],
]);
} catch (Exception $e) {
logger()->error($e);

return response()->json([
'success' => false,
'message' => 'Error has occurred while detecting change.',
]);
}
}

/**
* Filter out published state activity id's.
*
Expand Down Expand Up @@ -632,4 +687,14 @@ private function getPublishBatchSize($activityIds): float|int

return $batchSize;
}

private function validationCompleted(array $response): bool
{
return Arr::get($response, 'status') === 'completed';
}

private function bulkPublishCompleted(array $response): bool
{
return Arr::get($response, 'status') === 'completed';
}
}
36 changes: 35 additions & 1 deletion app/IATI/Repositories/Activity/ValidationStatusRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ public function getActivitiesValidationStatus(array $activityIds): array
$response['complete_count'] = 0;
$response['failed_count'] = 0;
$response['activities'] = [];
$response['activity_error_stats'] = [];

$validatorStatuses = $this->model->with('activity')->whereIn('activity_id', $activityIds)->get();

Expand Down Expand Up @@ -142,7 +143,11 @@ public function getActivitiesValidationStatus(array $activityIds): array

if ($validatorStatus->status === 'completed') {
$response['complete_count']++;
$result[$validatorStatus->activity_id]['is_valid'] = json_decode($validatorStatus->response, true)['response']['valid'];
$activityId = $validatorStatus->activity_id;
$responsePropertyOfValidatorResponse = Arr::get(json_decode($validatorStatus->response, true), 'response');
$result[$activityId]['is_valid'] = Arr::get($responsePropertyOfValidatorResponse, 'valid');
$responseSummary = Arr::get($responsePropertyOfValidatorResponse, 'summary', []);
$result[$activityId]['top_level_error'] = $this->getHighestSeverityErrorFromResponse($responseSummary);
}
}

Expand Down Expand Up @@ -250,4 +255,33 @@ public function insertInitialValidatorResponseDataForProperResponse(array $activ

$this->model->insert($insertableValidationStatus);
}

/**
* @param array $responseSummary
*
* @return string|null
*/
private function getHighestSeverityErrorFromResponse(array $responseSummary): ?string
{
$severityLevels = collect(['critical', 'error', 'warning', 'advisory']);

return $severityLevels->first(function ($level) use ($responseSummary) {
return Arr::get($responseSummary, $level, 0) > 0;
});
}

/**
* Returns array of activity_id of the activities that do not contain critical error.
*
* @param array $activityIds
*
* @return array
*/
public function getActivitiesWithNoCriticalErrors(array $activityIds): array
{
return $this->model->whereIn('activity_id', $activityIds)
->where('status', 'completed')
->whereRaw("(response->'response'->'summary'->>'critical')::integer = 0")
->pluck('activity_id')->toArray();
}
}
12 changes: 12 additions & 0 deletions app/IATI/Services/Workflow/BulkPublishingService.php
Original file line number Diff line number Diff line change
Expand Up @@ -505,4 +505,16 @@ public function getActivitiesWithDeprecatedValueArray(mixed $activityIds, Activi

return $returnArr;
}

/**
* Returns array of activity_id of the activities that do not contain critical error.
*
* @param array $activityIds
*
* @return array
*/
public function getPublishableActivityIds(array $activityIds): array
{
return $this->validationStatusRepository->getActivitiesWithNoCriticalErrors($activityIds);
}
}
Loading

0 comments on commit a17d277

Please sign in to comment.