254 lines
12 KiB
PHP
254 lines
12 KiB
PHP
<?php
|
|
|
|
/**
|
|
* @file ReviewReportPlugin.php
|
|
*
|
|
* Copyright (c) 2014-2020 Simon Fraser University
|
|
* Copyright (c) 2003-2020 John Willinsky
|
|
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
|
*
|
|
* @class ReviewReportPlugin
|
|
*
|
|
* @ingroup plugins_reports_review
|
|
*
|
|
* @see ReviewReportDAO
|
|
*
|
|
* @brief Review report plugin
|
|
*/
|
|
|
|
namespace APP\plugins\reports\reviewReport;
|
|
|
|
use APP\core\Application;
|
|
use PKP\core\PKPString;
|
|
use PKP\db\DAORegistry;
|
|
use PKP\plugins\ReportPlugin;
|
|
use PKP\reviewForm\ReviewFormElementDAO;
|
|
use PKP\reviewForm\ReviewFormResponseDAO;
|
|
use PKP\submission\reviewAssignment\ReviewAssignment;
|
|
use PKP\submission\reviewAssignment\ReviewAssignmentDAO;
|
|
use PKP\workflow\WorkflowStageDAO;
|
|
|
|
class ReviewReportPlugin extends ReportPlugin
|
|
{
|
|
/**
|
|
* @copydoc Plugin::register()
|
|
*
|
|
* @param null|mixed $mainContextId
|
|
*/
|
|
public function register($category, $path, $mainContextId = null)
|
|
{
|
|
$success = parent::register($category, $path, $mainContextId);
|
|
if (Application::isUnderMaintenance()) {
|
|
return $success;
|
|
}
|
|
|
|
$reviewReportDAO = new ReviewReportDAO();
|
|
DAORegistry::registerDAO('ReviewReportDAO', $reviewReportDAO);
|
|
$this->addLocaleData();
|
|
return $success;
|
|
}
|
|
|
|
/**
|
|
* @copydoc Plugin::getName()
|
|
*/
|
|
public function getName()
|
|
{
|
|
return 'ReviewReportPlugin';
|
|
}
|
|
|
|
/**
|
|
* @copydoc Plugin::getDisplayName()
|
|
*/
|
|
public function getDisplayName()
|
|
{
|
|
return __('plugins.reports.reviews.displayName');
|
|
}
|
|
|
|
/**
|
|
* @copydoc Plugin::getDescription()
|
|
*/
|
|
public function getDescription()
|
|
{
|
|
return __('plugins.reports.reviews.description');
|
|
}
|
|
|
|
/**
|
|
* @copydoc ReportPlugin::display()
|
|
*/
|
|
public function display($args, $request)
|
|
{
|
|
$context = $request->getContext();
|
|
|
|
header('content-type: text/comma-separated-values');
|
|
header('content-disposition: attachment; filename=reviews-' . date('Ymd') . '.csv');
|
|
|
|
$reviewReportDao = DAORegistry::getDAO('ReviewReportDAO'); /** @var ReviewReportDAO $reviewReportDao */
|
|
[$commentsIterator, $reviewsIterator, $interestsArray] = $reviewReportDao->getReviewReport($context->getId());
|
|
|
|
$comments = [];
|
|
foreach ($commentsIterator as $row) {
|
|
if (isset($comments[$row->submission_id][$row->author_id])) {
|
|
$comments[$row->submission_id][$row->author_id] .= '; ' . $row->comments;
|
|
} else {
|
|
$comments[$row->submission_id][$row->author_id] = $row->comments;
|
|
}
|
|
}
|
|
|
|
$recommendations = [
|
|
ReviewAssignment::SUBMISSION_REVIEWER_RECOMMENDATION_ACCEPT => 'reviewer.article.decision.accept',
|
|
ReviewAssignment::SUBMISSION_REVIEWER_RECOMMENDATION_PENDING_REVISIONS => 'reviewer.article.decision.pendingRevisions',
|
|
ReviewAssignment::SUBMISSION_REVIEWER_RECOMMENDATION_RESUBMIT_HERE => 'reviewer.article.decision.resubmitHere',
|
|
ReviewAssignment::SUBMISSION_REVIEWER_RECOMMENDATION_RESUBMIT_ELSEWHERE => 'reviewer.article.decision.resubmitElsewhere',
|
|
ReviewAssignment::SUBMISSION_REVIEWER_RECOMMENDATION_DECLINE => 'reviewer.article.decision.decline',
|
|
ReviewAssignment::SUBMISSION_REVIEWER_RECOMMENDATION_SEE_COMMENTS => 'reviewer.article.decision.seeComments'
|
|
];
|
|
|
|
$considerations = [
|
|
ReviewAssignment::REVIEW_ASSIGNMENT_NEW => 'plugins.reports.reviews.considered.new',
|
|
ReviewAssignment::REVIEW_ASSIGNMENT_CONSIDERED => 'plugins.reports.reviews.considered.considered',
|
|
ReviewAssignment::REVIEW_ASSIGNMENT_UNCONSIDERED => 'plugins.reports.reviews.considered.unconsidered',
|
|
ReviewAssignment::REVIEW_ASSIGNMENT_RECONSIDERED => 'plugins.reports.reviews.considered.reconsidered',
|
|
];
|
|
|
|
$columns = [
|
|
'stage_id' => __('workflow.stage'),
|
|
'round' => __('plugins.reports.reviews.round'),
|
|
'submission' => __('plugins.reports.reviews.submissionTitle'),
|
|
'submission_id' => __('plugins.reports.reviews.submissionId'),
|
|
'reviewer' => __('plugins.reports.reviews.reviewer'),
|
|
'user_given' => __('user.givenName'),
|
|
'user_family' => __('user.familyName'),
|
|
'orcid' => __('user.orcid'),
|
|
'country' => __('common.country'),
|
|
'affiliation' => __('user.affiliation'),
|
|
'email' => __('user.email'),
|
|
'interests' => __('user.interests'),
|
|
'date_assigned' => __('plugins.reports.reviews.dateAssigned'),
|
|
'date_notified' => __('plugins.reports.reviews.dateNotified'),
|
|
'date_confirmed' => __('plugins.reports.reviews.dateConfirmed'),
|
|
'date_completed' => __('plugins.reports.reviews.dateCompleted'),
|
|
'date_acknowledged' => __('plugins.reports.reviews.dateAcknowledged'),
|
|
'considered' => __('plugins.reports.reviews.considered'),
|
|
'date_reminded' => __('plugins.reports.reviews.dateReminded'),
|
|
'date_response_due' => __('reviewer.submission.responseDueDate'),
|
|
'overdue_response' => __('plugins.reports.reviews.responseOverdue'),
|
|
'date_due' => __('reviewer.submission.reviewDueDate'),
|
|
'overdue' => __('plugins.reports.reviews.reviewOverdue'),
|
|
'declined' => __('submissions.declined'),
|
|
'cancelled' => __('common.cancelled'),
|
|
'recommendation' => __('plugins.reports.reviews.recommendation'),
|
|
'comments' => __('plugins.reports.reviews.comments')
|
|
];
|
|
|
|
$fp = fopen('php://output', 'wt');
|
|
//Add BOM (byte order mark) to fix UTF-8 in Excel
|
|
fprintf($fp, chr(0xEF) . chr(0xBB) . chr(0xBF));
|
|
fputcsv($fp, array_values($columns));
|
|
|
|
/** @var ReviewAssignmentDAO */
|
|
$reviewAssignmentDao = DAORegistry::getDAO('ReviewAssignmentDAO');
|
|
/** @var ReviewFormResponseDAO */
|
|
$reviewFormResponseDao = DAORegistry::getDAO('ReviewFormResponseDAO');
|
|
/** @var ReviewFormElementDAO */
|
|
$reviewFormElementDao = DAORegistry::getDAO('ReviewFormElementDAO');
|
|
|
|
foreach ($reviewsIterator as $row) {
|
|
if (substr($row->date_response_due, 11) === '00:00:00') {
|
|
$row->date_response_due = substr($row->date_response_due, 0, 11) . '23:59:59';
|
|
}
|
|
if (substr($row->date_due, 11) === '00:00:00') {
|
|
$row->date_due = substr($row->date_due, 0, 11) . '23:59:59';
|
|
}
|
|
[$overdueResponseDays, $overdueDays] = $this->getOverdueDays($row);
|
|
$row->overdue_response = $overdueResponseDays;
|
|
$row->overdue = $overdueDays;
|
|
|
|
foreach ($columns as $index => $junk) {
|
|
switch ($index) {
|
|
case 'stage_id':
|
|
$columns[$index] = __(WorkflowStageDAO::getTranslationKeyFromId($row->$index));
|
|
break;
|
|
case 'declined':
|
|
case 'cancelled':
|
|
$columns[$index] = __($row->$index ? 'common.yes' : 'common.no');
|
|
break;
|
|
case 'considered':
|
|
$columns[$index] = isset($considerations[$row->$index]) ? __($considerations[$row->$index]) : '';
|
|
break;
|
|
case 'recommendation':
|
|
$columns[$index] = isset($recommendations[$row->$index]) ? __($recommendations[$row->$index]) : '';
|
|
break;
|
|
case 'comments':
|
|
$reviewAssignment = $reviewAssignmentDao->getById($row->review_id);
|
|
$body = '';
|
|
|
|
if ($reviewAssignment->getDateCompleted() != null && ($reviewFormId = $reviewAssignment->getReviewFormId())) {
|
|
$reviewId = $reviewAssignment->getId();
|
|
$reviewFormElements = $reviewFormElementDao->getByReviewFormId($reviewFormId);
|
|
while ($reviewFormElement = $reviewFormElements->next()) {
|
|
if (!$reviewFormElement->getIncluded()) {
|
|
continue;
|
|
}
|
|
$body .= PKPString::stripUnsafeHtml($reviewFormElement->getLocalizedQuestion());
|
|
$reviewFormResponse = $reviewFormResponseDao->getReviewFormResponse($reviewId, $reviewFormElement->getId());
|
|
if ($reviewFormResponse) {
|
|
$possibleResponses = $reviewFormElement->getLocalizedPossibleResponses();
|
|
if (in_array($reviewFormElement->getElementType(), [$reviewFormElement::REVIEW_FORM_ELEMENT_TYPE_CHECKBOXES, $reviewFormElement::REVIEW_FORM_ELEMENT_TYPE_RADIO_BUTTONS])) {
|
|
ksort($possibleResponses);
|
|
$possibleResponses = array_values($possibleResponses);
|
|
}
|
|
if (in_array($reviewFormElement->getElementType(), $reviewFormElement->getMultipleResponsesElementTypes())) {
|
|
if ($reviewFormElement->getElementType() == $reviewFormElement::REVIEW_FORM_ELEMENT_TYPE_CHECKBOXES) {
|
|
$body .= '<ul>';
|
|
foreach ($reviewFormResponse->getValue() as $value) {
|
|
$body .= '<li>' . PKPString::stripUnsafeHtml($possibleResponses[$value]) . '</li>';
|
|
}
|
|
$body .= '</ul>';
|
|
} else {
|
|
$body .= '<blockquote>' . PKPString::stripUnsafeHtml($possibleResponses[$reviewFormResponse->getValue()]) . '</blockquote>';
|
|
}
|
|
$body .= '<br>';
|
|
} else {
|
|
$body .= '<blockquote>' . nl2br(htmlspecialchars($reviewFormResponse->getValue())) . '</blockquote>';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
$columns[$index] = $comments[$row->submission_id][$row->reviewer_id] ?? $body;
|
|
break;
|
|
case 'interests':
|
|
$columns[$index] = $interestsArray[$row->reviewer_id] ?? '';
|
|
break;
|
|
default:
|
|
$columns[$index] = $row->$index;
|
|
}
|
|
}
|
|
fputcsv($fp, $columns);
|
|
}
|
|
fclose($fp);
|
|
}
|
|
|
|
public function getOverdueDays($row)
|
|
{
|
|
$responseDueTime = strtotime($row->date_response_due);
|
|
$reviewDueTime = strtotime($row->date_due);
|
|
$overdueResponseDays = $overdueDays = '';
|
|
if (!$row->date_confirmed) { // no response
|
|
if ($responseDueTime < time()) { // response overdue
|
|
$datediff = time() - $responseDueTime;
|
|
$overdueResponseDays = round($datediff / (60 * 60 * 24));
|
|
} elseif ($reviewDueTime < time()) { // review overdue but not response
|
|
$datediff = time() - $reviewDueTime;
|
|
$overdueDays = round($datediff / (60 * 60 * 24));
|
|
}
|
|
} elseif (!$row->date_completed) { // response given, but not completed
|
|
if ($reviewDueTime < time()) { // review due
|
|
$datediff = time() - $reviewDueTime;
|
|
$overdueDays = round($datediff / (60 * 60 * 24));
|
|
}
|
|
}
|
|
return [$overdueResponseDays, $overdueDays];
|
|
}
|
|
}
|