From b9f58cfc7072f9d3a2c7f6d61fbf5f4e7181f70a Mon Sep 17 00:00:00 2001 From: taslangraham Date: Fri, 21 Nov 2025 08:33:01 -0500 Subject: [PATCH 1/6] initial --- .../PublicationPeerReviewController.php | 23 +++++++ .../PublicationPeerReviewSummaryResource.php | 61 +++++++++++++++++++ .../submission/reviewAssignment/Collector.php | 18 ++++++ 3 files changed, 102 insertions(+) create mode 100644 api/v1/publicationPeerReviews/resources/PublicationPeerReviewSummaryResource.php diff --git a/api/v1/publicationPeerReviews/PublicationPeerReviewController.php b/api/v1/publicationPeerReviews/PublicationPeerReviewController.php index 7e3a7b6cb31..5c04a906b15 100644 --- a/api/v1/publicationPeerReviews/PublicationPeerReviewController.php +++ b/api/v1/publicationPeerReviews/PublicationPeerReviewController.php @@ -22,6 +22,7 @@ use Illuminate\Http\Request; use Illuminate\Http\Response; use Illuminate\Support\Facades\Route; +use PKP\API\v1\publicationPeerReviews\resources\PublicationPeerReviewSummaryResource; use PKP\core\PKPBaseController; use PKP\core\PKPRequest; use PKP\security\authorization\PublicReviewsEnabledPolicy; @@ -64,6 +65,10 @@ public function getGroupRoutes(): void Route::get('{publicationId}', $this->getOpenReview(...)) ->name('publicationPeerReviews.get') ->whereNumber('publicationId'); + + Route::get('{publicationId}/summary', $this->getPublicationReviewSummary(...)) + ->name('publicationPeerReviews.publication.summary') + ->whereNumber('publicationId'); }); } @@ -124,4 +129,22 @@ public function getOpenReview(Request $illuminateRequest): JsonResponse Response::HTTP_OK ); } + + public function getPublicationReviewSummary(Request $illuminateRequest): JsonResponse + { + $publicationId = (int)$illuminateRequest->route('publicationId'); + $publication = Repo::publication()->get($publicationId); + + if (!$publication) { + return response()->json([ + 'error' => __('api.404.resourceNotFound'), + ], Response::HTTP_NOT_FOUND); + } + + + return response()->json( + new PublicationPeerReviewSummaryResource($publication), + Response::HTTP_OK + ); + } } diff --git a/api/v1/publicationPeerReviews/resources/PublicationPeerReviewSummaryResource.php b/api/v1/publicationPeerReviews/resources/PublicationPeerReviewSummaryResource.php new file mode 100644 index 00000000000..ebd9c2ee51f --- /dev/null +++ b/api/v1/publicationPeerReviews/resources/PublicationPeerReviewSummaryResource.php @@ -0,0 +1,61 @@ +getCollector() + ->filterByPublicationId($this->getId()) + ->getMany() + ->groupBy(fn (ReviewAssignment $reviewAssignment, int $key) => $reviewAssignment->getReviewRoundId()) + // For each round, filter out reviews that has no response from reviewer. This way, a reviewer's last response will be the one reflected in final summary count + ->map(function ($assignments) { + return $assignments->filter(fn (ReviewAssignment $ra) => !!$ra->getDateCompleted()); + }) + ->sortKeys(); + + $reviewerResponseCount = collect(); + foreach ($reviewsGroupedByRoundId as $key => $reviews) { + /** @var ReviewAssignment $review */ + foreach ($reviews as $review) { + // For each review in each round, record the reviewer's decision, overriding any decision from previous rounds + $reviewerResponseCount->put($review->getReviewerId(), $review->getReviewerRecommendationId()); + } + } + + $reviewerResponseCount = $reviewerResponseCount->countBy(); + $summaryCount = []; + + foreach (ReviewerRecommendation::all() as $recommendationType) { + $count = $reviewerResponseCount->get($recommendationType->id, 0); + $summaryCount[] = [ + 'recommendationTypeId' => $recommendationType->id, + 'recommendationTyeText' => $recommendationType->getLocalizedData('title'), + 'count' => $count, + ]; + } + + $submission = Repo::submission()->get($this->getData('submissionId')); + $currentPublication = $submission->getCurrentPublication(); + $publishedPublications = $submission->getPublishedPublications(); + + return [ + 'reviewResponseCount' => $summaryCount, + // Number of published versions of the publication's submission + 'publishedVersions' => count($publishedPublications), + // Latest published publication for the submission associated with this publication + 'currentVersion' => $currentPublication ? [ + 'title' => $currentPublication->getLocalizedTitle(), + 'datePublished' => $currentPublication->getData('datePublished'), + ] : null + ]; + } + +} diff --git a/classes/submission/reviewAssignment/Collector.php b/classes/submission/reviewAssignment/Collector.php index eff56311555..dfb91ee815e 100644 --- a/classes/submission/reviewAssignment/Collector.php +++ b/classes/submission/reviewAssignment/Collector.php @@ -58,6 +58,7 @@ class Collector implements CollectorInterface, ViewsCount public ?string $orderByContextIdDirection = null; public bool $orderBySubmissionId = false; public ?string $orderBySubmissionIdDirection = null; + public ?int $publicationId = null; public function __construct(DAO $dao) { @@ -239,6 +240,12 @@ public function filterByReviewFormIds(?array $reviewFormIds): static return $this; } + public function filterByPublicationId(?int $publicationId): static + { + // Used to filter review assignments by publication id via review rounds + $this->publicationId = $publicationId; + return $this; + } /** * Filter by recommendations */ @@ -324,6 +331,17 @@ public function getQueryBuilder(): Builder $q->whereIn('ra.submission_id', $this->submissionIds) ); + $q->when( + $this->publicationId !== null, + fn (Builder $q) => $q + ->whereExists( + fn (Builder $sq) => $sq + ->from('review_rounds AS rr') + ->whereColumn('rr.review_round_id', 'ra.review_round_id') + ->where('rr.publication_id', $this->publicationId) + ) + ); + $q->when($this->isLastReviewRound || $this->isIncomplete, function (Builder $q) { $q // Aggregating data regarding latest review round and stage. For OMP the latest round isn't equal to the round with the highest number per submission From 18f24c04545dc96ba7212955b0b4084093eb8c4c Mon Sep 17 00:00:00 2001 From: taslangraham Date: Mon, 24 Nov 2025 11:24:26 -0500 Subject: [PATCH 2/6] pkp/pkp-lib#12044 Add API endpoint to get publication peer review summary --- .../PublicationPeerReviewController.php | 38 +++++++++++++++++++ .../PublicationPeerReviewSummaryResource.php | 5 ++- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/api/v1/publicationPeerReviews/PublicationPeerReviewController.php b/api/v1/publicationPeerReviews/PublicationPeerReviewController.php index 5c04a906b15..ca9ceb9dcc2 100644 --- a/api/v1/publicationPeerReviews/PublicationPeerReviewController.php +++ b/api/v1/publicationPeerReviews/PublicationPeerReviewController.php @@ -66,6 +66,9 @@ public function getGroupRoutes(): void ->name('publicationPeerReviews.get') ->whereNumber('publicationId'); + Route::get('/summary', $this->getPublicationReviewSummary(...)) + ->name('publicationPeerReviews.publication.summary'); + Route::get('{publicationId}/summary', $this->getPublicationReviewSummary(...)) ->name('publicationPeerReviews.publication.summary') ->whereNumber('publicationId'); @@ -147,4 +150,39 @@ public function getPublicationReviewSummary(Request $illuminateRequest): JsonRes Response::HTTP_OK ); } + + public function getManyPublicationReviewSummaries(Request $illuminateRequest): JsonResponse + { + $publicationIdsRaw = paramToArray($illuminateRequest->query('publicationIds', [])); + $publicationIds = []; + + foreach ($publicationIdsRaw as $id) { + if (!filter_var($id, FILTER_VALIDATE_INT)) { + return response()->json([ + 'error' => __('api.publication.400.invalidPublicationId', ['publicationId' => $id]) + ], Response::HTTP_BAD_REQUEST); + } + + $publicationIds[] = (int)$id; + } + + $publications = Repo::publication()->getCollector() + ->filterByPublicationIds($publicationIds) + ->getMany(); + + if ($publications->count() != count($publicationIds)) { + return response()->json([ + 'error' => __('api.404.resourceNotFound'), + ], Response::HTTP_NOT_FOUND); + } + + $summaries = $publications->map(function ($publication) { + return new PublicationPeerReviewSummaryResource($publication); + }); + + return response()->json( + $summaries->all(), + Response::HTTP_OK + ); + } } diff --git a/api/v1/publicationPeerReviews/resources/PublicationPeerReviewSummaryResource.php b/api/v1/publicationPeerReviews/resources/PublicationPeerReviewSummaryResource.php index ebd9c2ee51f..4524e597d4f 100644 --- a/api/v1/publicationPeerReviews/resources/PublicationPeerReviewSummaryResource.php +++ b/api/v1/publicationPeerReviews/resources/PublicationPeerReviewSummaryResource.php @@ -47,7 +47,9 @@ public function toArray(?\Illuminate\Http\Request $request = null) $publishedPublications = $submission->getPublishedPublications(); return [ - 'reviewResponseCount' => $summaryCount, + 'publicationId' => $this->getId(), + 'versionString' => $this->getData('versionString'), + 'summaryCount' => $summaryCount, // Number of published versions of the publication's submission 'publishedVersions' => count($publishedPublications), // Latest published publication for the submission associated with this publication @@ -57,5 +59,4 @@ public function toArray(?\Illuminate\Http\Request $request = null) ] : null ]; } - } From 294409c10c69184176399e56548f227766cae933 Mon Sep 17 00:00:00 2001 From: taslangraham Date: Tue, 25 Nov 2025 13:32:03 -0500 Subject: [PATCH 3/6] pkp/pkp-lib#12044Include summary count when returning peer reviews for a publication # Conflicts: # api/v1/publicationPeerReviews/resources/PublicationPeerReviewResource.php --- .../PublicationPeerReviewResource.php | 64 +++++++++++++++---- 1 file changed, 52 insertions(+), 12 deletions(-) diff --git a/api/v1/publicationPeerReviews/resources/PublicationPeerReviewResource.php b/api/v1/publicationPeerReviews/resources/PublicationPeerReviewResource.php index c9ffbb52280..92aac9f9047 100644 --- a/api/v1/publicationPeerReviews/resources/PublicationPeerReviewResource.php +++ b/api/v1/publicationPeerReviews/resources/PublicationPeerReviewResource.php @@ -28,6 +28,7 @@ use PKP\reviewForm\ReviewFormElementDAO; use PKP\reviewForm\ReviewFormResponseDAO; use PKP\submission\reviewAssignment\ReviewAssignment; +use PKP\submission\reviewer\recommendation\ReviewerRecommendation; use PKP\submission\reviewRound\ReviewRoundDAO; use PKP\submission\SubmissionComment; use PKP\submission\SubmissionCommentDAO; @@ -38,14 +39,14 @@ public function toArray(?\Illuminate\Http\Request $request = null) { /** @var Publication $publication */ $publication = $this->resource; + $publicationReviewsData = $this->getPublicationPeerReview($publication); return [ 'publicationId' => $publication->getId(), 'datePublished' => $publication->getData('datePublished'), - 'reviewRounds' => $this->getPublicationPeerReview($publication) + 'reviewRounds' => $publicationReviewsData->get('roundsData'), + 'summaryCount' => $publicationReviewsData->get('summaryCount') ]; } - - /** * Get public peer review data for a publication. * @@ -54,6 +55,7 @@ public function toArray(?\Illuminate\Http\Request $request = null) */ private function getPublicationPeerReview(Publication $publication): Enumerable { + $results = collect(); // Check up the tree on source IDs $allAssociatedPublicationIds = Repo::publication()->getWithSourcePublicationsIds([$publication->getId()]); @@ -66,29 +68,67 @@ private function getPublicationPeerReview(Publication $publication): Enumerable $hasMultipleRounds = $reviewRounds->getCount() > 1; $roundsData = collect(); - while ($reviewRound = $reviewRounds->next()) { - $assignments = Repo::reviewAssignment() - ->getCollector() - ->filterByReviewRoundIds([$reviewRound->getData('id')]) - ->getMany(); + $reviewRoundsKeyedById = collect($reviewRounds->toArray())->keyBy(fn ($item) => $item->getId()); + $roundIds = $reviewRoundsKeyedById->keys()->all(); + + $reviewsGroupedByRoundId = Repo::reviewAssignment() + ->getCollector() + ->filterByReviewRoundIds($roundIds) + ->getMany() + ->groupBy(fn (ReviewAssignment $ra) => $ra->getReviewRoundId()) + ->sortKeys(); + foreach ($reviewsGroupedByRoundId as $roundId => $assignments) { $roundDisplayText = $hasMultipleRounds ? __('publication.versionStringWithRound', [ 'versionString' => $publication->getData('versionString'), - 'round' => $reviewRound->getData('round') + 'round' => $reviewRoundsKeyedById->get($roundId)->getData('round') ]) : $publication->getData('versionString'); $roundsData->add([ 'displayText' => $roundDisplayText, - 'roundId' => $reviewRound->getData('id'), - 'originalPublicationId' => $reviewRound->getPublicationId(), + 'roundId' => $reviewRoundsKeyedById->get($roundId)->getData('id'), + 'originalPublicationId' => $reviewRoundsKeyedById->get($roundId)->getPublicationId(), 'reviews' => $this->getReviewAssignmentPeerReviews($assignments, $context), ]); } - return $roundsData; + + $results->put('roundsData', $roundsData); + $results->put('summaryCount', $this->buildSummaryCount($this->getLatestReviewerResponses($reviewsGroupedByRoundId))); + return $results; } + private function getLatestReviewerResponses($reviewsGrouped) + { + $responses = collect(); + + foreach ($reviewsGrouped as $reviews) { + foreach ($reviews as $review) { + $responses->put( + $review->getReviewerId(), + $review->getReviewerRecommendationId() + ); + } + } + + return $responses->countBy(); + } + + private function buildSummaryCount(Enumerable $reviewerResponseCount) + { + $summary = []; + + foreach (ReviewerRecommendation::all() as $type) { + $summary[] = [ + 'recommendationTypeId' => $type->id, + 'recommendationTypeText' => $type->getLocalizedData('title'), + 'count' => $reviewerResponseCount->get($type->id, 0), + ]; + } + + return $summary; + } /** * Get public peer review specific data for a list of review assignments. * From ca0ee7e086c5a9c08a5b26a43ae969d78295e8ec Mon Sep 17 00:00:00 2001 From: taslangraham Date: Tue, 25 Nov 2025 14:00:38 -0500 Subject: [PATCH 4/6] pkp/pkp-lib#12044 Place all open peer reviews endpoints in single controller and with unified `/peerReviews` path --- .../peerReviewController.php} | 94 ++++++++++++++++--- .../PublicationPeerReviewResource.php | 4 +- .../PublicationPeerReviewSummaryResource.php | 2 +- .../SubmissionPeerReviewSummaryResource.php | 87 +++++++++++++++++ classes/publication/Repository.php | 2 +- 5 files changed, 172 insertions(+), 17 deletions(-) rename api/v1/{publicationPeerReviews/PublicationPeerReviewController.php => peerReviews/peerReviewController.php} (63%) rename api/v1/{publicationPeerReviews => peerReviews}/resources/PublicationPeerReviewResource.php (98%) rename api/v1/{publicationPeerReviews => peerReviews}/resources/PublicationPeerReviewSummaryResource.php (98%) create mode 100644 api/v1/peerReviews/resources/SubmissionPeerReviewSummaryResource.php diff --git a/api/v1/publicationPeerReviews/PublicationPeerReviewController.php b/api/v1/peerReviews/peerReviewController.php similarity index 63% rename from api/v1/publicationPeerReviews/PublicationPeerReviewController.php rename to api/v1/peerReviews/peerReviewController.php index ca9ceb9dcc2..13109c2dd68 100644 --- a/api/v1/publicationPeerReviews/PublicationPeerReviewController.php +++ b/api/v1/peerReviews/peerReviewController.php @@ -1,33 +1,35 @@ group(function () { + Route::prefix('open/publications')->group(function () { Route::get('/', $this->getManyOpenReviews(...)) - ->name('publicationPeerReviews.getManyOpenReviews'); + ->name('peerReviews.getManyOpenReviews'); Route::get('{publicationId}', $this->getOpenReview(...)) - ->name('publicationPeerReviews.get') + ->name('peerReviews.get') ->whereNumber('publicationId'); Route::get('/summary', $this->getPublicationReviewSummary(...)) - ->name('publicationPeerReviews.publication.summary'); + ->name('peerReviews.publication.summary'); Route::get('{publicationId}/summary', $this->getPublicationReviewSummary(...)) - ->name('publicationPeerReviews.publication.summary') + ->name('peerReviews.publication.summary') ->whereNumber('publicationId'); }); - } + Route::prefix('open/submissions')->group(function () { + Route::get('{submissionId}/summary', $this->getSubmissionPeerReviewSummary(...)) + ->name('peerReviews.open.submissions.summary') + ->whereNumber('submissionId'); + + Route::get('summary', $this->getManySubmissionPeerReviewSummary(...)); + }); + } /** * Get peer review for a list of publications * Filters available via query params: @@ -185,4 +194,63 @@ public function getManyPublicationReviewSummaries(Request $illuminateRequest): J Response::HTTP_OK ); } + + public function getSubmissionPeerReviewSummary(Request $illuminateRequest): JsonResponse + { + $publicationId = (int)$illuminateRequest->route('publicationId'); + $publication = Repo::publication()->get($publicationId); + + $submissionId = (int)$illuminateRequest->route('submissionId'); + $submission = Repo::submission()->get($submissionId); + + if (!$submission) { + return response()->json([ + 'error' => __('api.404.resourceNotFound'), + ], Response::HTTP_NOT_FOUND); + } + + return response()->json( + new SubmissionPeerReviewSummaryResource($submission), + Response::HTTP_OK + ); + } + + public function getManySubmissionPeerReviewSummary(Request $illuminateRequest) + { + $submissionIdsRaw = paramToArray($illuminateRequest->query('submissionIds', [])); + $submissionIds = []; + + foreach ($submissionIdsRaw as $id) { + if (!filter_var($id, FILTER_VALIDATE_INT)) { + // TODO add submission locale + return response()->json([ + 'error' => __('api.publication.400.invalidPublicationId', ['publicationId' => $id]) + ], Response::HTTP_BAD_REQUEST); + } + + $submissionIds[] = (int)$id; + } + + $submissions = Repo::submission()->getCollector() + ->filterByContextIds([Application::SITE_CONTEXT_ID_ALL]) + ->filterBySubmissionIds($submissionIds)->getMany(); + + if ($submissions->count() != count($submissionIds)) { + return response()->json([ + 'error' => __('api.404.resourceNotFound'), + ], Response::HTTP_NOT_FOUND); + } + + + + + $summaries = $submissions->map(function ($submission) { + return new SubmissionPeerReviewSummaryResource($submission); + }); + + return response()->json( + $summaries->values(), + Response::HTTP_OK + ); + } } diff --git a/api/v1/publicationPeerReviews/resources/PublicationPeerReviewResource.php b/api/v1/peerReviews/resources/PublicationPeerReviewResource.php similarity index 98% rename from api/v1/publicationPeerReviews/resources/PublicationPeerReviewResource.php rename to api/v1/peerReviews/resources/PublicationPeerReviewResource.php index 92aac9f9047..34107d7ba56 100644 --- a/api/v1/publicationPeerReviews/resources/PublicationPeerReviewResource.php +++ b/api/v1/peerReviews/resources/PublicationPeerReviewResource.php @@ -1,7 +1,7 @@ resource; + + $reviewsGrouped = $this->getCompletedReviewsGroupedByRound($submission); + $reviewerResponseCount = $this->getLatestReviewerResponses($reviewsGrouped); + $summaryCount = $this->buildSummaryCount($reviewerResponseCount); + + return [ + 'submissionId' => $submission->getId(), + 'summaryCount' => $summaryCount, + 'publishedVersions' => count($submission->getPublishedPublications()), + 'currentVersion' => $this->getCurrentVersionData($submission), + ]; + } + + private function getCompletedReviewsGroupedByRound(Submission $submission) + { + return Repo::reviewAssignment()->getCollector() + ->filterBySubmissionIds([$submission->getId()]) + ->getMany() + ->groupBy(fn (ReviewAssignment $ra) => $ra->getReviewRoundId()) + ->map( + fn ($assignments) => + $assignments->filter(fn (ReviewAssignment $ra) => !!$ra->getDateCompleted()) + ) + ->sortKeys(); + } + + private function getLatestReviewerResponses($reviewsGrouped) + { + $responses = collect(); + + foreach ($reviewsGrouped as $reviews) { + foreach ($reviews as $review) { + $responses->put( + $review->getReviewerId(), + $review->getReviewerRecommendationId() + ); + } + } + + return $responses->countBy(); + } + private function buildSummaryCount(Enumerable $reviewerResponseCount) + { + $summary = []; + + foreach (ReviewerRecommendation::all() as $type) { + $summary[] = [ + 'recommendationTypeId' => $type->id, + 'recommendationTypeText' => $type->getLocalizedData('title'), + 'count' => $reviewerResponseCount->get($type->id, 0), + ]; + } + + return $summary; + } + private function getCurrentVersionData(Submission $submission) + { + $publication = $submission->getCurrentPublication(); + + if (!$publication) { + return null; + } + + return [ + 'title' => $publication->getLocalizedTitle(), + 'datePublished' => $publication->getData('datePublished'), + ]; + } +} diff --git a/classes/publication/Repository.php b/classes/publication/Repository.php index 1409a9ad00c..8f909d7cb7d 100644 --- a/classes/publication/Repository.php +++ b/classes/publication/Repository.php @@ -24,7 +24,7 @@ use APP\submission\Submission; use Illuminate\Support\Collection; use Illuminate\Support\Enumerable; -use PKP\API\v1\publicationPeerReviews\resources\PublicationPeerReviewResource; +use PKP\API\v1\peerReviews\resources\PublicationPeerReviewResource; use PKP\context\Context; use PKP\core\Core; use PKP\core\PKPApplication; From 69973d89dd100891bb3df0bed9ce044583865605 Mon Sep 17 00:00:00 2001 From: taslangraham Date: Tue, 25 Nov 2025 15:07:19 -0500 Subject: [PATCH 5/6] pkp/pkp-lib#12044 Move summary count logic into base resource class --- api/v1/peerReviews/peerReviewController.php | 2 +- .../resources/BasePeerReviewResource.php | 75 +++++++++++++++++ .../PublicationPeerReviewResource.php | 47 +++-------- .../PublicationPeerReviewSummaryResource.php | 48 +++-------- .../SubmissionPeerReviewSummaryResource.php | 81 +++---------------- .../submission/reviewAssignment/Collector.php | 10 +-- .../reviewAssignment/Repository.php | 9 +++ .../reviewAssignment/ReviewAssignment.php | 2 + .../recommendation/ReviewerRecommendation.php | 7 ++ 9 files changed, 131 insertions(+), 150 deletions(-) create mode 100644 api/v1/peerReviews/resources/BasePeerReviewResource.php diff --git a/api/v1/peerReviews/peerReviewController.php b/api/v1/peerReviews/peerReviewController.php index 13109c2dd68..d79bcc30e58 100644 --- a/api/v1/peerReviews/peerReviewController.php +++ b/api/v1/peerReviews/peerReviewController.php @@ -24,7 +24,7 @@ use Illuminate\Http\Response; use Illuminate\Support\Facades\Route; use PKP\API\v1\peerReviews\resources\PublicationPeerReviewSummaryResource; -use PKP\API\v1\submissions\peerReviews\SubmissionPeerReviewSummaryResource; +use PKP\API\v1\peerReviews\resources\SubmissionPeerReviewSummaryResource; use PKP\core\PKPBaseController; use PKP\core\PKPRequest; use PKP\security\authorization\PublicReviewsEnabledPolicy; diff --git a/api/v1/peerReviews/resources/BasePeerReviewResource.php b/api/v1/peerReviews/resources/BasePeerReviewResource.php new file mode 100644 index 00000000000..fdb4591d9b4 --- /dev/null +++ b/api/v1/peerReviews/resources/BasePeerReviewResource.php @@ -0,0 +1,75 @@ +groupBy(fn (ReviewAssignment $ra) => $ra->getReviewRoundId()) + ->map( + fn ($assignments) => + $assignments->filter(fn (ReviewAssignment $ra) => !!$ra->getDateCompleted()) + ) + ->sortKeys(); + + return $this->getSummaryCountForReviewerRecommendation($reviewAssignmentsGroupedByRoundId); + } + + private function getSummaryCountForReviewerRecommendation(Enumerable $reviewAssignmentsGroupedByRoundId): array + { + $responses = collect(); + + $availableRecommendationsGroupedByType = ReviewerRecommendation::withContextId(1)->get(); + + foreach ($reviewAssignmentsGroupedByRoundId as $reviews) { + /** @var ReviewAssignment $review */ + foreach ($reviews as $review) { + // For each review in each round, record the reviewer's decision, overriding any decision from previous rounds + // Therefore keeping their latest recommendation + $responses->put( + $review->getReviewerId(), + $availableRecommendationsGroupedByType->get($review->getReviewerRecommendationId())->recommendationType, + ); + } + } + + return $this->buildSummaryCount($responses->countBy()); + } + + private function buildSummaryCount(Enumerable $reviewerResponseCount): array + { + $summary = []; + + foreach (ReviewerRecommendation::all() as $type) { + $summary[] = [ + 'recommendationTypeId' => $type->id, + 'recommendationTypeText' => $type->getLocalizedData('title'), + 'count' => $reviewerResponseCount->get($type->id, 0), + ]; + } + + $summary = []; + + // Group by type + $recTypes = ReviewerRecommendation::withContextId(1)->get()->groupBy('type'); + $recommendationTypeLabels = Repo::reviewerRecommendation()->getRecommendationTypeLabels(); + // go through each group + foreach ($recTypes as $typeId => $recommendation) { + $summary[] = [ + 'recommendationTypeId' => $typeId, + 'recommendationTypeLabel' => $recommendationTypeLabels[$typeId], + 'count' => $reviewerResponseCount->get($typeId, 0), + ]; + } + // get count and locale for each + return $summary; + } +} diff --git a/api/v1/peerReviews/resources/PublicationPeerReviewResource.php b/api/v1/peerReviews/resources/PublicationPeerReviewResource.php index 34107d7ba56..33456317d7e 100644 --- a/api/v1/peerReviews/resources/PublicationPeerReviewResource.php +++ b/api/v1/peerReviews/resources/PublicationPeerReviewResource.php @@ -19,7 +19,6 @@ use APP\core\Application; use APP\facades\Repo; use APP\publication\Publication; -use Illuminate\Http\Resources\Json\JsonResource; use Illuminate\Support\Enumerable; use PKP\context\Context; use PKP\db\DAORegistry; @@ -28,12 +27,11 @@ use PKP\reviewForm\ReviewFormElementDAO; use PKP\reviewForm\ReviewFormResponseDAO; use PKP\submission\reviewAssignment\ReviewAssignment; -use PKP\submission\reviewer\recommendation\ReviewerRecommendation; use PKP\submission\reviewRound\ReviewRoundDAO; use PKP\submission\SubmissionComment; use PKP\submission\SubmissionCommentDAO; -class PublicationPeerReviewResource extends JsonResource +class PublicationPeerReviewResource extends BasePeerReviewResource { public function toArray(?\Illuminate\Http\Request $request = null) { @@ -44,7 +42,7 @@ public function toArray(?\Illuminate\Http\Request $request = null) 'publicationId' => $publication->getId(), 'datePublished' => $publication->getData('datePublished'), 'reviewRounds' => $publicationReviewsData->get('roundsData'), - 'summaryCount' => $publicationReviewsData->get('summaryCount') + 'reviewerRecommendationsSummary' => $publicationReviewsData->get('reviewerRecommendationsSummary'), ]; } /** @@ -56,6 +54,7 @@ public function toArray(?\Illuminate\Http\Request $request = null) private function getPublicationPeerReview(Publication $publication): Enumerable { $results = collect(); + // Check up the tree on source IDs $allAssociatedPublicationIds = Repo::publication()->getWithSourcePublicationsIds([$publication->getId()]); @@ -65,16 +64,20 @@ private function getPublicationPeerReview(Publication $publication): Enumerable $context = app()->get('context')->get( Repo::submission()->get($publication->getData('submissionId'))->getData('contextId') ); + $hasMultipleRounds = $reviewRounds->getCount() > 1; $roundsData = collect(); $reviewRoundsKeyedById = collect($reviewRounds->toArray())->keyBy(fn ($item) => $item->getId()); $roundIds = $reviewRoundsKeyedById->keys()->all(); + unset($reviewRounds); - $reviewsGroupedByRoundId = Repo::reviewAssignment() + $reviewAssignments = Repo::reviewAssignment() ->getCollector() ->filterByReviewRoundIds($roundIds) - ->getMany() + ->getMany(); + + $reviewsGroupedByRoundId = $reviewAssignments ->groupBy(fn (ReviewAssignment $ra) => $ra->getReviewRoundId()) ->sortKeys(); @@ -95,40 +98,10 @@ private function getPublicationPeerReview(Publication $publication): Enumerable $results->put('roundsData', $roundsData); - $results->put('summaryCount', $this->buildSummaryCount($this->getLatestReviewerResponses($reviewsGroupedByRoundId))); + $results->put('reviewerRecommendationsSummary', $this->getReviewerRecommendationsSummary($reviewAssignments)); return $results; } - private function getLatestReviewerResponses($reviewsGrouped) - { - $responses = collect(); - - foreach ($reviewsGrouped as $reviews) { - foreach ($reviews as $review) { - $responses->put( - $review->getReviewerId(), - $review->getReviewerRecommendationId() - ); - } - } - - return $responses->countBy(); - } - - private function buildSummaryCount(Enumerable $reviewerResponseCount) - { - $summary = []; - - foreach (ReviewerRecommendation::all() as $type) { - $summary[] = [ - 'recommendationTypeId' => $type->id, - 'recommendationTypeText' => $type->getLocalizedData('title'), - 'count' => $reviewerResponseCount->get($type->id, 0), - ]; - } - - return $summary; - } /** * Get public peer review specific data for a list of review assignments. * diff --git a/api/v1/peerReviews/resources/PublicationPeerReviewSummaryResource.php b/api/v1/peerReviews/resources/PublicationPeerReviewSummaryResource.php index e5f8f64a931..38f22c51a60 100644 --- a/api/v1/peerReviews/resources/PublicationPeerReviewSummaryResource.php +++ b/api/v1/peerReviews/resources/PublicationPeerReviewSummaryResource.php @@ -3,44 +3,17 @@ namespace PKP\API\v1\peerReviews\resources; use APP\facades\Repo; -use Illuminate\Http\Resources\Json\JsonResource; -use PKP\submission\reviewAssignment\ReviewAssignment; -use PKP\submission\reviewer\recommendation\ReviewerRecommendation; -class PublicationPeerReviewSummaryResource extends JsonResource +class PublicationPeerReviewSummaryResource extends BasePeerReviewResource { - public function toArray(?\Illuminate\Http\Request $request = null) + public function toArray(\Illuminate\Http\Request $request) { - $reviewsGroupedByRoundId = Repo::reviewAssignment()->getCollector() - ->filterByPublicationId($this->getId()) - ->getMany() - ->groupBy(fn (ReviewAssignment $reviewAssignment, int $key) => $reviewAssignment->getReviewRoundId()) - // For each round, filter out reviews that has no response from reviewer. This way, a reviewer's last response will be the one reflected in final summary count - ->map(function ($assignments) { - return $assignments->filter(fn (ReviewAssignment $ra) => !!$ra->getDateCompleted()); - }) - ->sortKeys(); + $allAssociatedPublicationIds = Repo::publication()->getWithSourcePublicationsIds([$this->getId()])->all(); - $reviewerResponseCount = collect(); - foreach ($reviewsGroupedByRoundId as $key => $reviews) { - /** @var ReviewAssignment $review */ - foreach ($reviews as $review) { - // For each review in each round, record the reviewer's decision, overriding any decision from previous rounds - $reviewerResponseCount->put($review->getReviewerId(), $review->getReviewerRecommendationId()); - } - } - - $reviewerResponseCount = $reviewerResponseCount->countBy(); - $summaryCount = []; - - foreach (ReviewerRecommendation::all() as $recommendationType) { - $count = $reviewerResponseCount->get($recommendationType->id, 0); - $summaryCount[] = [ - 'recommendationTypeId' => $recommendationType->id, - 'recommendationTyeText' => $recommendationType->getLocalizedData('title'), - 'count' => $count, - ]; - } + // Include reviews from the Publication's Source Publication so that are are to be copied forward are accounted for. + $reviewAssignments = Repo::reviewAssignment()->getCollector() + ->filterByPublicationIds($allAssociatedPublicationIds) + ->getMany(); $submission = Repo::submission()->get($this->getData('submissionId')); $currentPublication = $submission->getCurrentPublication(); @@ -48,12 +21,11 @@ public function toArray(?\Illuminate\Http\Request $request = null) return [ 'publicationId' => $this->getId(), - 'versionString' => $this->getData('versionString'), - 'summaryCount' => $summaryCount, + 'reviewerRecommendations' => $this->getReviewerRecommendationsSummary($reviewAssignments), // Number of published versions of the publication's submission - 'publishedVersions' => count($publishedPublications), + 'submissionPublishedVersionsCount' => count($publishedPublications), // Latest published publication for the submission associated with this publication - 'currentVersion' => $currentPublication ? [ + 'submissionCurrentVersion' => $currentPublication ? [ 'title' => $currentPublication->getLocalizedTitle(), 'datePublished' => $currentPublication->getData('datePublished'), ] : null diff --git a/api/v1/peerReviews/resources/SubmissionPeerReviewSummaryResource.php b/api/v1/peerReviews/resources/SubmissionPeerReviewSummaryResource.php index bb28309c012..2b8bd93026b 100644 --- a/api/v1/peerReviews/resources/SubmissionPeerReviewSummaryResource.php +++ b/api/v1/peerReviews/resources/SubmissionPeerReviewSummaryResource.php @@ -1,87 +1,30 @@ resource; - - $reviewsGrouped = $this->getCompletedReviewsGroupedByRound($submission); - $reviewerResponseCount = $this->getLatestReviewerResponses($reviewsGrouped); - $summaryCount = $this->buildSummaryCount($reviewerResponseCount); - - return [ - 'submissionId' => $submission->getId(), - 'summaryCount' => $summaryCount, - 'publishedVersions' => count($submission->getPublishedPublications()), - 'currentVersion' => $this->getCurrentVersionData($submission), - ]; - } - - private function getCompletedReviewsGroupedByRound(Submission $submission) - { - return Repo::reviewAssignment()->getCollector() + $reviewAssignments = Repo::reviewAssignment()->getCollector() ->filterBySubmissionIds([$submission->getId()]) - ->getMany() - ->groupBy(fn (ReviewAssignment $ra) => $ra->getReviewRoundId()) - ->map( - fn ($assignments) => - $assignments->filter(fn (ReviewAssignment $ra) => !!$ra->getDateCompleted()) - ) - ->sortKeys(); - } - - private function getLatestReviewerResponses($reviewsGrouped) - { - $responses = collect(); - - foreach ($reviewsGrouped as $reviews) { - foreach ($reviews as $review) { - $responses->put( - $review->getReviewerId(), - $review->getReviewerRecommendationId() - ); - } - } - - return $responses->countBy(); - } - private function buildSummaryCount(Enumerable $reviewerResponseCount) - { - $summary = []; - - foreach (ReviewerRecommendation::all() as $type) { - $summary[] = [ - 'recommendationTypeId' => $type->id, - 'recommendationTypeText' => $type->getLocalizedData('title'), - 'count' => $reviewerResponseCount->get($type->id, 0), - ]; - } - - return $summary; - } - private function getCurrentVersionData(Submission $submission) - { - $publication = $submission->getCurrentPublication(); - - if (!$publication) { - return null; - } + ->getMany(); + $currentPublication = $submission->getCurrentPublication(); return [ - 'title' => $publication->getLocalizedTitle(), - 'datePublished' => $publication->getData('datePublished'), + 'submissionId' => $submission->getId(), + 'reviewerRecommendations' => $this->getReviewerRecommendationsSummary($reviewAssignments), + 'publishedVersionsCount' => count($submission->getPublishedPublications()), + 'currentVersion' => $currentPublication ? [ + 'title' => $currentPublication->getLocalizedTitle(), + 'datePublished' => $currentPublication->getData('datePublished'), + ] : null, ]; } } diff --git a/classes/submission/reviewAssignment/Collector.php b/classes/submission/reviewAssignment/Collector.php index dfb91ee815e..ae1eb56bb8d 100644 --- a/classes/submission/reviewAssignment/Collector.php +++ b/classes/submission/reviewAssignment/Collector.php @@ -58,7 +58,7 @@ class Collector implements CollectorInterface, ViewsCount public ?string $orderByContextIdDirection = null; public bool $orderBySubmissionId = false; public ?string $orderBySubmissionIdDirection = null; - public ?int $publicationId = null; + public ?array $publicationIds = null; public function __construct(DAO $dao) { @@ -240,10 +240,10 @@ public function filterByReviewFormIds(?array $reviewFormIds): static return $this; } - public function filterByPublicationId(?int $publicationId): static + public function filterByPublicationIds(?array $publicationIds): static { // Used to filter review assignments by publication id via review rounds - $this->publicationId = $publicationId; + $this->publicationIds = $publicationIds; return $this; } /** @@ -332,13 +332,13 @@ public function getQueryBuilder(): Builder ); $q->when( - $this->publicationId !== null, + $this->publicationIds !== null, fn (Builder $q) => $q ->whereExists( fn (Builder $sq) => $sq ->from('review_rounds AS rr') ->whereColumn('rr.review_round_id', 'ra.review_round_id') - ->where('rr.publication_id', $this->publicationId) + ->whereIn('rr.publication_id', $this->publicationIds) ) ); diff --git a/classes/submission/reviewAssignment/Repository.php b/classes/submission/reviewAssignment/Repository.php index 2a74f0fca54..5b1981632c4 100644 --- a/classes/submission/reviewAssignment/Repository.php +++ b/classes/submission/reviewAssignment/Repository.php @@ -1,4 +1,5 @@ dao->getExternalReviewerIdsByCompletedYear($contextId, $year); } + + /** + * Get summarised reviewer recommendations from a list of given review assignments + */ + public function getReviewerRecommendationsSummary(array $reviewAssignments) + { + + } } diff --git a/classes/submission/reviewAssignment/ReviewAssignment.php b/classes/submission/reviewAssignment/ReviewAssignment.php index 8e299afe918..78b36cedca3 100644 --- a/classes/submission/reviewAssignment/ReviewAssignment.php +++ b/classes/submission/reviewAssignment/ReviewAssignment.php @@ -236,6 +236,8 @@ public function getReviewerRecommendationId(): ?int return $this->getData('reviewerRecommendationId'); } + + /** * Set reviewer recommendation id */ diff --git a/classes/submission/reviewer/recommendation/ReviewerRecommendation.php b/classes/submission/reviewer/recommendation/ReviewerRecommendation.php index 2b77e42cf33..4434dd3cd2e 100644 --- a/classes/submission/reviewer/recommendation/ReviewerRecommendation.php +++ b/classes/submission/reviewer/recommendation/ReviewerRecommendation.php @@ -109,6 +109,13 @@ protected function removable(): Attribute )->shouldCache(); } + protected function recommendationType(): Attribute + { + return Attribute::make( + get: fn () => $this->type, + )->shouldCache(); + } + /** * Get associated context details as attribute */ From 76c4b72f791dff8f2e67818222ad996464a09c4a Mon Sep 17 00:00:00 2001 From: taslangraham Date: Wed, 10 Dec 2025 09:37:06 -0500 Subject: [PATCH 6/6] pkp/pkp-lib#12044 Code cleanup --- api/v1/peerReviews/peerReviewController.php | 60 ++++++++++-------- .../resources/BasePeerReviewResource.php | 62 ++++++++++++------- .../PublicationPeerReviewResource.php | 12 ++-- .../PublicationPeerReviewSummaryResource.php | 34 ++++++++-- .../SubmissionPeerReviewSummaryResource.php | 25 +++++++- .../submission/reviewAssignment/Collector.php | 4 +- .../reviewAssignment/Repository.php | 8 --- .../reviewAssignment/ReviewAssignment.php | 2 - .../recommendation/ReviewerRecommendation.php | 7 --- locale/en/api.po | 3 + 10 files changed, 136 insertions(+), 81 deletions(-) diff --git a/api/v1/peerReviews/peerReviewController.php b/api/v1/peerReviews/peerReviewController.php index d79bcc30e58..a9d5be6a059 100644 --- a/api/v1/peerReviews/peerReviewController.php +++ b/api/v1/peerReviews/peerReviewController.php @@ -31,6 +31,9 @@ class peerReviewController extends PKPBaseController { + /** + * @copyDoc + */ public function authorize(PKPRequest $request, array &$args, array $roleAssignments): bool { $this->addPolicy(new PublicReviewsEnabledPolicy($request->getContext())); @@ -64,24 +67,25 @@ public function getGroupRoutes(): void Route::get('/', $this->getManyOpenReviews(...)) ->name('peerReviews.getManyOpenReviews'); + Route::get('summary', $this->getManyPublicationReviewSummaries(...)) + ->name('peerReviews.publication.summary.getMany'); + Route::get('{publicationId}', $this->getOpenReview(...)) ->name('peerReviews.get') ->whereNumber('publicationId'); - Route::get('/summary', $this->getPublicationReviewSummary(...)) - ->name('peerReviews.publication.summary'); - Route::get('{publicationId}/summary', $this->getPublicationReviewSummary(...)) - ->name('peerReviews.publication.summary') + ->name('peerReviews.publication.summary.get') ->whereNumber('publicationId'); }); Route::prefix('open/submissions')->group(function () { Route::get('{submissionId}/summary', $this->getSubmissionPeerReviewSummary(...)) - ->name('peerReviews.open.submissions.summary') + ->name('peerReviews.open.submissions.summary.get') ->whereNumber('submissionId'); - Route::get('summary', $this->getManySubmissionPeerReviewSummary(...)); + Route::get('summary', $this->getManySubmissionPeerReviewSummary(...)) + ->name('eerReviews.open.submissions.summary.getMany'); }); } /** @@ -142,6 +146,9 @@ public function getOpenReview(Request $illuminateRequest): JsonResponse ); } + /** + * Get peer review summary by publication ID + */ public function getPublicationReviewSummary(Request $illuminateRequest): JsonResponse { $publicationId = (int)$illuminateRequest->route('publicationId'); @@ -153,13 +160,15 @@ public function getPublicationReviewSummary(Request $illuminateRequest): JsonRes ], Response::HTTP_NOT_FOUND); } - return response()->json( new PublicationPeerReviewSummaryResource($publication), Response::HTTP_OK ); } + /** + * Get peer review summaries for a list of publication IDs + */ public function getManyPublicationReviewSummaries(Request $illuminateRequest): JsonResponse { $publicationIdsRaw = paramToArray($illuminateRequest->query('publicationIds', [])); @@ -185,23 +194,22 @@ public function getManyPublicationReviewSummaries(Request $illuminateRequest): J ], Response::HTTP_NOT_FOUND); } - $summaries = $publications->map(function ($publication) { - return new PublicationPeerReviewSummaryResource($publication); - }); - return response()->json( - $summaries->all(), + PublicationPeerReviewSummaryResource::collection($publications), Response::HTTP_OK ); } + /** + * Get peer review summary by submission ID. + */ public function getSubmissionPeerReviewSummary(Request $illuminateRequest): JsonResponse { - $publicationId = (int)$illuminateRequest->route('publicationId'); - $publication = Repo::publication()->get($publicationId); + $request = Application::get()->getRequest(); + $context = $request->getContext(); $submissionId = (int)$illuminateRequest->route('submissionId'); - $submission = Repo::submission()->get($submissionId); + $submission = Repo::submission()->get($submissionId, $context->getId()); if (!$submission) { return response()->json([ @@ -215,16 +223,21 @@ public function getSubmissionPeerReviewSummary(Request $illuminateRequest): Json ); } - public function getManySubmissionPeerReviewSummary(Request $illuminateRequest) + /** + * Get peer review summaries for a list of submission IDs + */ + public function getManySubmissionPeerReviewSummary(Request $illuminateRequest): JsonResponse { $submissionIdsRaw = paramToArray($illuminateRequest->query('submissionIds', [])); $submissionIds = []; + $request = Application::get()->getRequest(); + $context = $request->getContext(); + foreach ($submissionIdsRaw as $id) { if (!filter_var($id, FILTER_VALIDATE_INT)) { - // TODO add submission locale return response()->json([ - 'error' => __('api.publication.400.invalidPublicationId', ['publicationId' => $id]) + 'error' => __('api.submission.400.invalidSubmissionId', ['submissionId' => $id]) ], Response::HTTP_BAD_REQUEST); } @@ -232,7 +245,7 @@ public function getManySubmissionPeerReviewSummary(Request $illuminateRequest) } $submissions = Repo::submission()->getCollector() - ->filterByContextIds([Application::SITE_CONTEXT_ID_ALL]) + ->filterByContextIds([$context->getId()]) ->filterBySubmissionIds($submissionIds)->getMany(); if ($submissions->count() != count($submissionIds)) { @@ -241,15 +254,8 @@ public function getManySubmissionPeerReviewSummary(Request $illuminateRequest) ], Response::HTTP_NOT_FOUND); } - - - - $summaries = $submissions->map(function ($submission) { - return new SubmissionPeerReviewSummaryResource($submission); - }); - return response()->json( - $summaries->values(), + SubmissionPeerReviewSummaryResource::collection($submissions), Response::HTTP_OK ); } diff --git a/api/v1/peerReviews/resources/BasePeerReviewResource.php b/api/v1/peerReviews/resources/BasePeerReviewResource.php index fdb4591d9b4..9a80cbb6843 100644 --- a/api/v1/peerReviews/resources/BasePeerReviewResource.php +++ b/api/v1/peerReviews/resources/BasePeerReviewResource.php @@ -1,16 +1,38 @@ groupBy(fn (ReviewAssignment $ra) => $ra->getReviewRoundId()) @@ -20,48 +42,43 @@ public function getReviewerRecommendationsSummary(Enumerable $reviewAssignments) ) ->sortKeys(); - return $this->getSummaryCountForReviewerRecommendation($reviewAssignmentsGroupedByRoundId); + return $this->getSummaryCountForReviewerRecommendation($reviewAssignmentsGroupedByRoundId, $context); } - private function getSummaryCountForReviewerRecommendation(Enumerable $reviewAssignmentsGroupedByRoundId): array + /** + * Get the summary count for reviews. + */ + private function getSummaryCountForReviewerRecommendation(Enumerable $reviewAssignmentsGroupedByRoundId, Context $context): array { $responses = collect(); - $availableRecommendationsGroupedByType = ReviewerRecommendation::withContextId(1)->get(); + $availableRecommendationsGroupedByType = ReviewerRecommendation::withContextId($context->getId())->get(); foreach ($reviewAssignmentsGroupedByRoundId as $reviews) { /** @var ReviewAssignment $review */ foreach ($reviews as $review) { - // For each review in each round, record the reviewer's decision, overriding any decision from previous rounds - // Therefore keeping their latest recommendation + // For each review in each round, record the reviewer's decision, overriding any decision from previous rounds, keeping their latest recommendation $responses->put( $review->getReviewerId(), - $availableRecommendationsGroupedByType->get($review->getReviewerRecommendationId())->recommendationType, + $availableRecommendationsGroupedByType->get($review->getReviewerRecommendationId())->type, ); } } - return $this->buildSummaryCount($responses->countBy()); + return $this->buildSummaryCount($responses->countBy(), $context); } - private function buildSummaryCount(Enumerable $reviewerResponseCount): array + /** + * Tally review recommendations for each Recommendation type + */ + private function buildSummaryCount(Enumerable $reviewerResponseCount, Context $context): array { $summary = []; - foreach (ReviewerRecommendation::all() as $type) { - $summary[] = [ - 'recommendationTypeId' => $type->id, - 'recommendationTypeText' => $type->getLocalizedData('title'), - 'count' => $reviewerResponseCount->get($type->id, 0), - ]; - } - - $summary = []; - - // Group by type - $recTypes = ReviewerRecommendation::withContextId(1)->get()->groupBy('type'); + $recTypes = ReviewerRecommendation::withContextId($context->getId()) + ->get() + ->groupBy('type'); $recommendationTypeLabels = Repo::reviewerRecommendation()->getRecommendationTypeLabels(); - // go through each group foreach ($recTypes as $typeId => $recommendation) { $summary[] = [ 'recommendationTypeId' => $typeId, @@ -69,7 +86,6 @@ private function buildSummaryCount(Enumerable $reviewerResponseCount): array 'count' => $reviewerResponseCount->get($typeId, 0), ]; } - // get count and locale for each return $summary; } } diff --git a/api/v1/peerReviews/resources/PublicationPeerReviewResource.php b/api/v1/peerReviews/resources/PublicationPeerReviewResource.php index 33456317d7e..ec3869ca9cd 100644 --- a/api/v1/peerReviews/resources/PublicationPeerReviewResource.php +++ b/api/v1/peerReviews/resources/PublicationPeerReviewResource.php @@ -9,7 +9,7 @@ * * @class PublicationPeerReviewResource * - * @ingroup api_v1_publicationPeerReviews + * @ingroup api_v1_peerReviews * * @brief Resource that maps a publication to its open peer reviews data */ @@ -82,23 +82,25 @@ private function getPublicationPeerReview(Publication $publication): Enumerable ->sortKeys(); foreach ($reviewsGroupedByRoundId as $roundId => $assignments) { + $reviewRound = $reviewRoundsKeyedById->get($roundId); + $roundDisplayText = $hasMultipleRounds ? __('publication.versionStringWithRound', [ 'versionString' => $publication->getData('versionString'), - 'round' => $reviewRoundsKeyedById->get($roundId)->getData('round') + 'round' => $reviewRound->getData('round') ]) : $publication->getData('versionString'); $roundsData->add([ 'displayText' => $roundDisplayText, - 'roundId' => $reviewRoundsKeyedById->get($roundId)->getData('id'), - 'originalPublicationId' => $reviewRoundsKeyedById->get($roundId)->getPublicationId(), + 'roundId' => $reviewRound->getData('id'), + 'originalPublicationId' => $reviewRound->getPublicationId(), 'reviews' => $this->getReviewAssignmentPeerReviews($assignments, $context), ]); } $results->put('roundsData', $roundsData); - $results->put('reviewerRecommendationsSummary', $this->getReviewerRecommendationsSummary($reviewAssignments)); + $results->put('reviewerRecommendationsSummary', $this->getReviewerRecommendationsSummary($reviewAssignments, $context)); return $results; } diff --git a/api/v1/peerReviews/resources/PublicationPeerReviewSummaryResource.php b/api/v1/peerReviews/resources/PublicationPeerReviewSummaryResource.php index 38f22c51a60..e03c10701b1 100644 --- a/api/v1/peerReviews/resources/PublicationPeerReviewSummaryResource.php +++ b/api/v1/peerReviews/resources/PublicationPeerReviewSummaryResource.php @@ -1,27 +1,51 @@ getWithSourcePublicationsIds([$this->getId()])->all(); + /** @var Publication $publication */ + $publication = $this->resource; + + $submission = Repo::submission()->get($publication->getData('submissionId')); + $contextDao = Application::getContextDAO(); + /** @var Context $context */ + $context = $contextDao->getById($submission->getData('contextId')); + + $allAssociatedPublicationIds = Repo::publication()->getWithSourcePublicationsIds([ $publication->getId()])->all(); - // Include reviews from the Publication's Source Publication so that are are to be copied forward are accounted for. + // Include reviews from the Publication's Source Publication so that reviews that are to be copied forward are accounted for. $reviewAssignments = Repo::reviewAssignment()->getCollector() ->filterByPublicationIds($allAssociatedPublicationIds) ->getMany(); - $submission = Repo::submission()->get($this->getData('submissionId')); $currentPublication = $submission->getCurrentPublication(); $publishedPublications = $submission->getPublishedPublications(); return [ - 'publicationId' => $this->getId(), - 'reviewerRecommendations' => $this->getReviewerRecommendationsSummary($reviewAssignments), + 'publicationId' => $publication->getId(), + 'reviewerRecommendations' => $this->getReviewerRecommendationsSummary($reviewAssignments, $context), // Number of published versions of the publication's submission 'submissionPublishedVersionsCount' => count($publishedPublications), // Latest published publication for the submission associated with this publication diff --git a/api/v1/peerReviews/resources/SubmissionPeerReviewSummaryResource.php b/api/v1/peerReviews/resources/SubmissionPeerReviewSummaryResource.php index 2b8bd93026b..821375b986b 100644 --- a/api/v1/peerReviews/resources/SubmissionPeerReviewSummaryResource.php +++ b/api/v1/peerReviews/resources/SubmissionPeerReviewSummaryResource.php @@ -1,7 +1,22 @@ filterBySubmissionIds([$submission->getId()]) ->getMany(); + $contextDao = Application::getContextDAO(); + /** @var Context $context */ + $context = $contextDao->getById($submission->getData('contextId')); + $currentPublication = $submission->getCurrentPublication(); return [ 'submissionId' => $submission->getId(), - 'reviewerRecommendations' => $this->getReviewerRecommendationsSummary($reviewAssignments), - 'publishedVersionsCount' => count($submission->getPublishedPublications()), - 'currentVersion' => $currentPublication ? [ + 'reviewerRecommendations' => $this->getReviewerRecommendationsSummary($reviewAssignments, $context), + 'submissionPublishedVersionsCount' => count($submission->getPublishedPublications()), + 'submissionCurrentVersion' => $currentPublication ? [ 'title' => $currentPublication->getLocalizedTitle(), 'datePublished' => $currentPublication->getData('datePublished'), ] : null, diff --git a/classes/submission/reviewAssignment/Collector.php b/classes/submission/reviewAssignment/Collector.php index ae1eb56bb8d..fc6f5865f13 100644 --- a/classes/submission/reviewAssignment/Collector.php +++ b/classes/submission/reviewAssignment/Collector.php @@ -240,9 +240,11 @@ public function filterByReviewFormIds(?array $reviewFormIds): static return $this; } + /** + * Filter review assignments by associated publication IDs. + */ public function filterByPublicationIds(?array $publicationIds): static { - // Used to filter review assignments by publication id via review rounds $this->publicationIds = $publicationIds; return $this; } diff --git a/classes/submission/reviewAssignment/Repository.php b/classes/submission/reviewAssignment/Repository.php index 5b1981632c4..806ae64284f 100644 --- a/classes/submission/reviewAssignment/Repository.php +++ b/classes/submission/reviewAssignment/Repository.php @@ -289,12 +289,4 @@ public function getExternalReviewerIdsByCompletedYear(int $contextId, string $ye { return $this->dao->getExternalReviewerIdsByCompletedYear($contextId, $year); } - - /** - * Get summarised reviewer recommendations from a list of given review assignments - */ - public function getReviewerRecommendationsSummary(array $reviewAssignments) - { - - } } diff --git a/classes/submission/reviewAssignment/ReviewAssignment.php b/classes/submission/reviewAssignment/ReviewAssignment.php index 78b36cedca3..8e299afe918 100644 --- a/classes/submission/reviewAssignment/ReviewAssignment.php +++ b/classes/submission/reviewAssignment/ReviewAssignment.php @@ -236,8 +236,6 @@ public function getReviewerRecommendationId(): ?int return $this->getData('reviewerRecommendationId'); } - - /** * Set reviewer recommendation id */ diff --git a/classes/submission/reviewer/recommendation/ReviewerRecommendation.php b/classes/submission/reviewer/recommendation/ReviewerRecommendation.php index 4434dd3cd2e..2b77e42cf33 100644 --- a/classes/submission/reviewer/recommendation/ReviewerRecommendation.php +++ b/classes/submission/reviewer/recommendation/ReviewerRecommendation.php @@ -109,13 +109,6 @@ protected function removable(): Attribute )->shouldCache(); } - protected function recommendationType(): Attribute - { - return Attribute::make( - get: fn () => $this->type, - )->shouldCache(); - } - /** * Get associated context details as attribute */ diff --git a/locale/en/api.po b/locale/en/api.po index cc9e26daa92..3177879c67f 100644 --- a/locale/en/api.po +++ b/locale/en/api.po @@ -404,6 +404,9 @@ msgstr "The citation you requested was not found." msgid "api.publication.400.invalidPublicationId" msgstr "An invalid publication ID was provided: `{$publicationId}`." +msgid "api.submission.400.invalidSubmissionId" +msgstr "An invalid submission ID was provided: `{$submissionId}`." + msgid "api.contributorRole.404.roleNotFound" msgstr "The role requested was not found."