Skip to content

Commit

Permalink
pkp#9965 Anonymize reviews for authors
Browse files Browse the repository at this point in the history
  • Loading branch information
Vitaliy-1 committed Jun 12, 2024
1 parent f42453e commit 0d3f1ba
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 52 deletions.
11 changes: 10 additions & 1 deletion api/v1/_submissions/PKPBackendSubmissionsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Route;
use PKP\API\v1\submissions\AnonymizeData;
use PKP\config\Config;
use PKP\core\PKPBaseController;
use PKP\core\PKPRequest;
Expand All @@ -38,6 +39,8 @@

abstract class PKPBackendSubmissionsController extends PKPBaseController
{
use AnonymizeData;

/** @var int Max items that can be requested */
public const MAX_COUNT = 100;

Expand Down Expand Up @@ -104,6 +107,7 @@ public function getGroupRoutes(): void
Role::ROLE_ID_MANAGER,
Role::ROLE_ID_SUB_EDITOR,
Role::ROLE_ID_ASSISTANT,
Role::ROLE_ID_AUTHOR,
]),
]);

Expand Down Expand Up @@ -259,7 +263,12 @@ public function assigned(Request $illuminateRequest): JsonResponse

return response()->json([
'itemsMax' => $collector->getCount(),
'items' => Repo::submission()->getSchemaMap()->mapManyToSubmissionsList($submissions, $userGroups, $genres)->values(),
'items' => Repo::submission()->getSchemaMap()->mapManyToSubmissionsList(
$submissions,
$userGroups,
$genres,
$this->anonymizeReviews($submissions)
)->values(),
], Response::HTTP_OK);
}

Expand Down
74 changes: 74 additions & 0 deletions api/v1/submissions/AnonymizeData.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<?php

/**
* @file api/v1/submissions/AnonymizeData.php
*
* Copyright (c) 2014-2024 Simon Fraser University
* Copyright (c) 2000-2024 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class AnonymizeData
*
* @ingroup api_v1_submission
*
* @brief Trait for anonymizing sensitive submission data.
*
*/

namespace PKP\API\v1\submissions;

use APP\facades\Repo;
use APP\submission\Submission;
use Illuminate\Support\Collection;
use Illuminate\Support\LazyCollection;
use PKP\core\PKPRequest;
use PKP\security\Role;
use PKP\stageAssignment\StageAssignment;
use PKP\submission\reviewAssignment\ReviewAssignment;

trait AnonymizeData
{
abstract public function getRequest(): PKPRequest;

/**
* Checks if sensitive review assignment data should be anonymized for authors and reviewers
*
* @param LazyCollection<Submission>|Submission $submissions the list of submissions with IDs as keys or a single submission
* @param ?LazyCollection<ReviewAssignment> $reviewAssignments
*
* @return false|Collection List of review IDs to anonymize or false;
*/
public function anonymizeReviews(LazyCollection|Submission $submissions, ?LazyCollection $reviewAssignments = null): false|Collection
{
$currentUser = $this->getRequest()->getUser();
$submissionIds = is_a($submissions, Submission::class) ? [$submissions->getId()] : $submissions->keys()->toArray();
$reviewAssignments = $reviewAssignments ?? Repo::reviewAssignment()->getCollector()->filterBySubmissionIds($submissionIds)->getMany();

$currentUserReviewAssignment = Repo::reviewAssignment()->getCollector()
->filterBySubmissionIds($submissionIds)
->filterByReviewerIds([$currentUser->getId()])
->getMany();

$currentUserStageAssignments = StageAssignment::withSubmissionIds($submissionIds)
->withUserId($currentUser->getId())
->get();

$isAuthor = $currentUserStageAssignments->contains(
function (StageAssignment $stageAssignment) {
$userGroup = Repo::userGroup()->get($stageAssignment->userGroupId);
return $userGroup->getRoleId() == Role::ROLE_ID_AUTHOR;
}
);

if ($currentUserReviewAssignment->isNotEmpty() || $isAuthor) {
$anonymizeReviews = $reviewAssignments->map(function (ReviewAssignment $reviewAssignment, int $reviewId) use ($currentUserReviewAssignment) {
if ($currentUserReviewAssignment->isNotEmpty() && $currentUserReviewAssignment->has($reviewId)) {
return false;
}
return $reviewAssignment->getReviewMethod() !== ReviewAssignment::SUBMISSION_REVIEW_METHOD_OPEN;
})->filter()->keys()->collect();
}

return !isset($anonymizeReviews) || $anonymizeReviews->isEmpty() ? false : $anonymizeReviews;
}
}
49 changes: 3 additions & 46 deletions api/v1/submissions/PKPSubmissionController.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,9 @@
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Collection;
use Illuminate\Support\Enumerable;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\LazyCollection;
use PKP\core\Core;
use PKP\core\PKPApplication;
use PKP\core\PKPBaseController;
Expand All @@ -48,22 +46,23 @@
use PKP\plugins\Hook;
use PKP\security\authorization\ContextAccessPolicy;
use PKP\security\authorization\DecisionWritePolicy;
use PKP\security\authorization\internal\SubmissionCompletePolicy;
use PKP\security\authorization\PublicationWritePolicy;
use PKP\security\authorization\StageRolePolicy;
use PKP\security\authorization\SubmissionAccessPolicy;
use PKP\security\authorization\UserRolesRequiredPolicy;
use PKP\security\authorization\internal\SubmissionCompletePolicy;
use PKP\security\Role;
use PKP\security\Validation;
use PKP\services\PKPSchemaService;
use PKP\stageAssignment\StageAssignment;
use PKP\submission\GenreDAO;
use PKP\submission\PKPSubmission;
use PKP\submission\reviewAssignment\ReviewAssignment;
use PKP\userGroup\UserGroup;

class PKPSubmissionController extends PKPBaseController
{
use AnonymizeData;

/** @var int The default number of items to return in one request */
public const DEFAULT_COUNT = 30;

Expand Down Expand Up @@ -1657,48 +1656,6 @@ public function addDecision(Request $illuminateRequest): JsonResponse
return response()->json(Repo::decision()->getSchemaMap()->map($decision), Response::HTTP_OK);
}

/**
* Checks if sensitive review assignment data should be anonymized for authors and reviewers
*
* @param LazyCollection<Submission>|Submission $submissions the list of submissions with IDs as keys or a single submission
* @param ?LazyCollection<ReviewAssignment> $reviewAssignments
*
* @return false|Collection List of review IDs to anonymize or false;
*/
public function anonymizeReviews(LazyCollection|Submission $submissions, ?LazyCollection $reviewAssignments = null): false|Collection
{
$currentUser = $this->getRequest()->getUser();
$submissionIds = is_a($submissions, Submission::class) ? [$submissions->getId()] : $submissions->keys()->toArray();
$reviewAssignments = $reviewAssignments ?? Repo::reviewAssignment()->getCollector()->filterBySubmissionIds($submissionIds)->getMany();

$currentUserReviewAssignment = Repo::reviewAssignment()->getCollector()
->filterBySubmissionIds($submissionIds)
->filterByReviewerIds([$currentUser->getId()])
->getMany();

$currentUserStageAssignments = StageAssignment::withSubmissionIds($submissionIds)
->withUserId($currentUser->getId())
->get();

$isAuthor = $currentUserStageAssignments->contains(
function (StageAssignment $stageAssignment) {
$userGroup = Repo::userGroup()->get($stageAssignment->userGroupId);
return $userGroup->getRoleId() == Role::ROLE_ID_AUTHOR;
}
);

if ($currentUserReviewAssignment->isNotEmpty() || $isAuthor) {
$anonymizeReviews = $reviewAssignments->map(function (ReviewAssignment $reviewAssignment, int $reviewId) use ($currentUserReviewAssignment) {
if ($currentUserReviewAssignment->isNotEmpty() && $currentUserReviewAssignment->has($reviewId)) {
return false;
}
return $reviewAssignment->getReviewMethod() !== ReviewAssignment::SUBMISSION_REVIEW_METHOD_OPEN;
})->filter()->keys()->collect();
}

return !isset($anonymizeReviews) || $anonymizeReviews->isEmpty() ? false : $anonymizeReviews;
}

protected function getFirstUserGroupInRole(Enumerable $userGroups, int $role): ?UserGroup
{
return $userGroups->first(fn (UserGroup $userGroup) => $userGroup->getRoleId() === $role);
Expand Down
17 changes: 12 additions & 5 deletions classes/submission/maps/Schema.php
Original file line number Diff line number Diff line change
Expand Up @@ -232,20 +232,21 @@ public function summarizeMany(Enumerable $collection, LazyCollection $userGroups
* @param Genre[] $genres The file genres in this context
* @param ?Enumerable $reviewAssignments review assignments associated with a submission
* @param ?Enumerable $stageAssignments stage assignments associated with a submission
* @param bool|Collection<int> $anonymizeReviews List of review assignment IDs to anonymize
*/
public function mapToSubmissionsList(
Submission $item,
LazyCollection $userGroups,
array $genres,
?Enumerable $reviewAssignments = null,
?Enumerable $stageAssignments = null,
bool|Collection $anonymizeReviews = false
): array {
$this->userGroups = $userGroups;
$this->genres = $genres;
$this->reviewAssignments = $reviewAssignments ?? Repo::reviewAssignment()->getCollector()->filterBySubmissionIds([$item->getId()])->getMany()->remember();
$this->stageAssignments = $stageAssignments ?? $this->getStageAssignmentsBySubmissions(collect([$item]), [Role::ROLE_ID_MANAGER, Role::ROLE_ID_SUB_EDITOR]);

return $this->mapByProperties($this->getSubmissionsListProps(), $item);
return $this->mapByProperties($this->getSubmissionsListProps(), $item, $anonymizeReviews);
}

/**
Expand All @@ -255,9 +256,14 @@ public function mapToSubmissionsList(
*
* @param LazyCollection<int,UserGroup> $userGroups The user groups in this context
* @param Genre[] $genres The file genres in this context
* @param bool|Collection<int> $anonymizeReviews List of review assignment IDs to anonymize
*/
public function mapManyToSubmissionsList(Enumerable $collection, LazyCollection $userGroups, array $genres): Enumerable
{
public function mapManyToSubmissionsList(
Enumerable $collection,
LazyCollection $userGroups,
array $genres,
bool|Collection $anonymizeReviews = false
): Enumerable {
$this->collection = $collection;
$this->userGroups = $userGroups;
$this->genres = $genres;
Expand All @@ -280,7 +286,8 @@ public function mapManyToSubmissionsList(Enumerable $collection, LazyCollection
$this->userGroups,
$this->genres,
$associatedReviewAssignments->get($item->getId()),
$associatedStageAssignment->get($item->getId())
$associatedStageAssignment->get($item->getId()),
$anonymizeReviews
)
);
}
Expand Down

0 comments on commit 0d3f1ba

Please sign in to comment.