first commit
This commit is contained in:
@@ -0,0 +1,371 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file plugins/reports/articles/ArticleReportPlugin.php
|
||||
*
|
||||
* Copyright (c) 2014-2021 Simon Fraser University
|
||||
* Copyright (c) 2003-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class ArticleReportPlugin
|
||||
*
|
||||
* @ingroup plugins_reports_article
|
||||
*
|
||||
* @brief Article report plugin
|
||||
*/
|
||||
|
||||
namespace APP\plugins\reports\articles;
|
||||
|
||||
use APP\decision\Decision;
|
||||
use APP\facades\Repo;
|
||||
use PKP\core\PKPString;
|
||||
use PKP\db\DAORegistry;
|
||||
use PKP\facades\Locale;
|
||||
use PKP\plugins\ReportPlugin;
|
||||
use PKP\security\Role;
|
||||
use PKP\stageAssignment\StageAssignmentDAO;
|
||||
use PKP\submission\PKPSubmission;
|
||||
use PKP\submission\SubmissionAgencyDAO;
|
||||
use PKP\submission\SubmissionDisciplineDAO;
|
||||
use PKP\submission\SubmissionKeywordDAO;
|
||||
use PKP\submission\SubmissionSubjectDAO;
|
||||
|
||||
class ArticleReportPlugin extends ReportPlugin
|
||||
{
|
||||
/**
|
||||
* @copydoc Plugin::register()
|
||||
*
|
||||
* @param null|mixed $mainContextId
|
||||
*/
|
||||
public function register($category, $path, $mainContextId = null)
|
||||
{
|
||||
$success = parent::register($category, $path, $mainContextId);
|
||||
$this->addLocaleData();
|
||||
return $success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of this plugin. The name must be unique within
|
||||
* its category.
|
||||
*
|
||||
* @return string name of plugin
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'ArticleReportPlugin';
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc Plugin::getDisplayName()
|
||||
*/
|
||||
public function getDisplayName()
|
||||
{
|
||||
return __('plugins.reports.articles.displayName');
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc Plugin::getDescriptionName()
|
||||
*/
|
||||
public function getDescription()
|
||||
{
|
||||
return __('plugins.reports.articles.description');
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc ReportPlugin::display()
|
||||
*/
|
||||
public function display($args, $request)
|
||||
{
|
||||
$context = $request->getContext();
|
||||
$acronym = PKPString::regexp_replace('/[^A-Za-z0-9 ]/', '', $context->getLocalizedAcronym());
|
||||
|
||||
// Prepare for UTF8-encoded CSV output.
|
||||
header('content-type: text/comma-separated-values');
|
||||
header('content-disposition: attachment; filename=articles-' . $acronym . '-' . date('Ymd') . '.csv');
|
||||
$fp = fopen('php://output', 'wt');
|
||||
// Add BOM (byte order mark) to fix UTF-8 in Excel
|
||||
fprintf($fp, chr(0xEF) . chr(0xBB) . chr(0xBF));
|
||||
|
||||
$stageAssignmentDao = DAORegistry::getDAO('StageAssignmentDAO'); /** @var StageAssignmentDAO $stageAssignmentDao */
|
||||
$submissionKeywordDao = DAORegistry::getDAO('SubmissionKeywordDAO'); /** @var SubmissionKeywordDAO $submissionKeywordDao */
|
||||
$submissionSubjectDao = DAORegistry::getDAO('SubmissionSubjectDAO'); /** @var SubmissionSubjectDAO $submissionSubjectDao */
|
||||
$submissionDisciplineDao = DAORegistry::getDAO('SubmissionDisciplineDAO'); /** @var SubmissionDisciplineDAO $submissionDisciplineDao */
|
||||
$submissionAgencyDao = DAORegistry::getDAO('SubmissionAgencyDAO'); /** @var SubmissionAgencyDAO $submissionAgencyDao */
|
||||
|
||||
$userGroups = Repo::userGroup()->getCollector()
|
||||
->filterByContextIds([$context->getId()])
|
||||
->getMany()
|
||||
->toArray();
|
||||
|
||||
$editorUserGroupIds = array_map(function ($userGroup) {
|
||||
return $userGroup->getId();
|
||||
}, array_filter($userGroups, function ($userGroup) {
|
||||
return in_array($userGroup->getRoleId(), [Role::ROLE_ID_MANAGER, Role::ROLE_ID_SUB_EDITOR]);
|
||||
}));
|
||||
|
||||
// Load the data from the database and store it in an array.
|
||||
// (This must be stored before display because we won't know the data
|
||||
// dimensions until it has all been loaded.)
|
||||
$results = $sectionTitles = [];
|
||||
$collector = Repo::submission()->getCollector()->filterByContextIds([$context->getId()]);
|
||||
$submissions = $collector->getMany();
|
||||
$maxAuthors = $maxEditors = $maxDecisions = 0;
|
||||
foreach ($submissions as $submission) {
|
||||
$publication = $submission->getCurrentPublication();
|
||||
$maxAuthors = max($maxAuthors, count($publication->getData('authors')));
|
||||
$editDecisions = Repo::decision()->getCollector()
|
||||
->filterBySubmissionIds([$submission->getId()])
|
||||
->getMany();
|
||||
|
||||
$statusMap = $submission->getStatusMap();
|
||||
|
||||
// Count the highest number of decisions per editor.
|
||||
$editDecisionsPerEditor = [];
|
||||
foreach ($editDecisions as $editDecision) {
|
||||
$editorId = $editDecision->getData('editorId');
|
||||
$editDecisionsPerEditor[$editorId] = ($editDecisionsPerEditor[$editorId] ?? 0) + 1;
|
||||
$maxDecisions = max($maxDecisions, $editDecisionsPerEditor[$editorId]);
|
||||
}
|
||||
|
||||
// Load editor and decision information
|
||||
$stageAssignmentsFactory = $stageAssignmentDao->getBySubmissionAndStageId($submission->getId());
|
||||
$editors = $editorsById = [];
|
||||
while ($stageAssignment = $stageAssignmentsFactory->next()) {
|
||||
$userId = $stageAssignment->getUserId();
|
||||
if (!in_array($stageAssignment->getUserGroupId(), $editorUserGroupIds)) {
|
||||
continue;
|
||||
}
|
||||
if (isset($editors[$userId])) {
|
||||
continue;
|
||||
}
|
||||
if (!isset($editorsById[$userId])) {
|
||||
$editor = Repo::user()->get($userId, true);
|
||||
$editorsById[$userId] = [
|
||||
$editor->getLocalizedGivenName(),
|
||||
$editor->getLocalizedFamilyName(),
|
||||
$editor->getData('orcid'),
|
||||
$editor->getEmail(),
|
||||
];
|
||||
}
|
||||
$editors[$userId] = $editorsById[$userId];
|
||||
$maxEditors = max($maxEditors, count($editors));
|
||||
}
|
||||
|
||||
// Load section title information
|
||||
$sectionId = $publication->getData('sectionId');
|
||||
if ($sectionId && !isset($sectionTitles[$sectionId])) {
|
||||
$section = Repo::section()->get($sectionId);
|
||||
$sectionTitles[$sectionId] = $section->getLocalizedTitle();
|
||||
}
|
||||
|
||||
$subjects = $submissionSubjectDao->getSubjects($submission->getCurrentPublication()->getId());
|
||||
$disciplines = $submissionDisciplineDao->getDisciplines($submission->getCurrentPublication()->getId());
|
||||
$keywords = $submissionKeywordDao->getKeywords($submission->getCurrentPublication()->getId());
|
||||
$agencies = $submissionAgencyDao->getAgencies($submission->getCurrentPublication()->getId());
|
||||
|
||||
// Store the submission results
|
||||
$results[] = [
|
||||
'submissionId' => $submission->getId(),
|
||||
'title' => htmlspecialchars($publication->getLocalizedFullTitle(null, 'html')),
|
||||
'abstract' => html_entity_decode(strip_tags($publication->getLocalizedData('abstract'))),
|
||||
'authors' => array_map(function ($author) {
|
||||
return [
|
||||
$author->getLocalizedGivenName(),
|
||||
$author->getLocalizedFamilyName(),
|
||||
$author->getData('orcid'),
|
||||
$author->getData('country'),
|
||||
$author->getLocalizedData('affiliation'),
|
||||
$author->getData('email'),
|
||||
$author->getData('url'),
|
||||
html_entity_decode(strip_tags($author->getLocalizedData('biography'))),
|
||||
];
|
||||
}, $publication->getData('authors')->values()->toArray()),
|
||||
'sectionTitle' => $sectionTitles[$sectionId] ?? '',
|
||||
'language' => $publication->getData('locale'),
|
||||
'coverage' => $publication->getLocalizedData('coverage'),
|
||||
'rights' => $publication->getLocalizedData('rights'),
|
||||
'source' => $publication->getLocalizedData('source'),
|
||||
'subjects' => join(', ', $subjects[Locale::getLocale()] ?? $subjects[$submission->getLocale()] ?? []),
|
||||
'type' => $publication->getLocalizedData('type'),
|
||||
'disciplines' => join(', ', $disciplines[Locale::getLocale()] ?? $disciplines[$submission->getLocale()] ?? []),
|
||||
'keywords' => join(', ', $keywords[Locale::getLocale()] ?? $keywords[$submission->getLocale()] ?? []),
|
||||
'agencies' => join(', ', $agencies[Locale::getLocale()] ?? $agencies[$submission->getLocale()] ?? []),
|
||||
'status' => $submission->getStatus() == PKPSubmission::STATUS_QUEUED ? $this->getStageLabel($submission->getStageId()) : __($statusMap[$submission->getStatus()]),
|
||||
'url' => $request->url(null, 'workflow', 'access', $submission->getId()),
|
||||
'doi' => $submission->getStoredPubId('doi'),
|
||||
'dateSubmitted' => $submission->getDateSubmitted(),
|
||||
'lastModified' => $submission->getLastModified(),
|
||||
'firstPublished' => $submission->getOriginalPublication()?->getData('datePublished') ?? '',
|
||||
'editors' => $editors,
|
||||
'decisions' => $editDecisions->toArray(),
|
||||
];
|
||||
}
|
||||
|
||||
// Build and display the column headers.
|
||||
$columns = [
|
||||
__('article.submissionId'),
|
||||
__('article.title'),
|
||||
__('article.abstract')
|
||||
];
|
||||
|
||||
$authorColumnCount = $editorColumnCount = $decisionColumnCount = 0;
|
||||
for ($a = 1; $a <= $maxAuthors; $a++) {
|
||||
$columns = array_merge($columns, $authorColumns = [
|
||||
__('user.givenName') . ' (' . __('user.role.author') . " {$a})",
|
||||
__('user.familyName') . ' (' . __('user.role.author') . " {$a})",
|
||||
__('user.orcid') . ' (' . __('user.role.author') . " {$a})",
|
||||
__('common.country') . ' (' . __('user.role.author') . " {$a})",
|
||||
__('user.affiliation') . ' (' . __('user.role.author') . " {$a})",
|
||||
__('user.email') . ' (' . __('user.role.author') . " {$a})",
|
||||
__('user.url') . ' (' . __('user.role.author') . " {$a})",
|
||||
__('user.biography') . ' (' . __('user.role.author') . " {$a})"
|
||||
]);
|
||||
$authorColumnCount = count($authorColumns);
|
||||
}
|
||||
|
||||
$columns = array_merge($columns, [
|
||||
__('section.title'),
|
||||
__('common.language'),
|
||||
__('article.coverage'),
|
||||
__('submission.rights'),
|
||||
__('submission.source'),
|
||||
__('common.subjects'),
|
||||
__('common.type'),
|
||||
__('search.discipline'),
|
||||
__('common.keywords'),
|
||||
__('submission.supportingAgencies'),
|
||||
__('common.status'),
|
||||
__('common.url'),
|
||||
__('metadata.property.displayName.doi'),
|
||||
__('common.dateSubmitted'),
|
||||
__('submission.lastModified'),
|
||||
__('submission.firstPublished'),
|
||||
]);
|
||||
|
||||
for ($e = 1; $e <= $maxEditors; $e++) {
|
||||
$columns = array_merge($columns, $editorColumns = [
|
||||
__('user.givenName') . ' (' . __('user.role.editor') . " {$e})",
|
||||
__('user.familyName') . ' (' . __('user.role.editor') . " {$e})",
|
||||
__('user.orcid') . ' (' . __('user.role.editor') . " {$e})",
|
||||
__('user.email') . ' (' . __('user.role.editor') . " {$e})",
|
||||
]);
|
||||
$editorColumnCount = count($editorColumns);
|
||||
for ($d = 1; $d <= $maxDecisions; $d++) {
|
||||
$columns = array_merge($columns, $decisionColumns = [
|
||||
__('submission.editorDecision') . " {$d} " . ' (' . __('user.role.editor') . " {$e})",
|
||||
__('common.dateDecided') . " {$d} " . ' (' . __('user.role.editor') . " {$e})"
|
||||
]);
|
||||
$decisionColumnCount = count($decisionColumns);
|
||||
}
|
||||
}
|
||||
fputcsv($fp, array_values($columns));
|
||||
|
||||
// Display the data rows.
|
||||
foreach ($results as $result) {
|
||||
$row = [];
|
||||
foreach ($result as $column => $value) {
|
||||
switch ($column) {
|
||||
case 'authors':
|
||||
for ($i = 0; $i < $maxAuthors; $i++) {
|
||||
$row = array_merge($row, $value[$i] ?? array_fill(0, $authorColumnCount, ''));
|
||||
}
|
||||
break;
|
||||
case 'editors':
|
||||
$editorIds = array_keys($value);
|
||||
$editorEntries = array_values($value);
|
||||
for ($i = 0; $i < $maxEditors; $i++) {
|
||||
$submissionHasThisEditor = isset($editorEntries[$i]);
|
||||
$row = array_merge($row, $submissionHasThisEditor ? $editorEntries[$i] : array_fill(0, $editorColumnCount, ''));
|
||||
for ($j = 0; $j < $maxDecisions; $j++) {
|
||||
if (!$submissionHasThisEditor) {
|
||||
$row = array_merge($row, array_fill(0, $decisionColumnCount, ''));
|
||||
continue;
|
||||
}
|
||||
|
||||
$editorId = $editorIds[$i];
|
||||
$latestDecision = $latestDecisionDate = '';
|
||||
$decisionCounter = 0;
|
||||
foreach ($result['decisions'] as $decision) {
|
||||
if ($decision->getData('editorId') != $editorId) {
|
||||
continue;
|
||||
}
|
||||
if ($j != $decisionCounter++) {
|
||||
continue;
|
||||
}
|
||||
$latestDecision = $this->getDecisionMessage($decision->getData('decision'));
|
||||
$latestDecisionDate = $decision->getData('dateDecided');
|
||||
}
|
||||
$row = array_merge($row, [$latestDecision, $latestDecisionDate]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'decisions':
|
||||
break; // Handled in the 'editors' case
|
||||
default: $row[] = $value; // Other columns can be sent as they are.
|
||||
}
|
||||
}
|
||||
fputcsv($fp, $row);
|
||||
}
|
||||
|
||||
fclose($fp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get stage label
|
||||
*
|
||||
* @param int $stageId WORKFLOW_STAGE_ID_...
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getStageLabel($stageId)
|
||||
{
|
||||
switch ($stageId) {
|
||||
case WORKFLOW_STAGE_ID_SUBMISSION:
|
||||
return __('submission.submission');
|
||||
case WORKFLOW_STAGE_ID_EXTERNAL_REVIEW:
|
||||
return __('submission.review');
|
||||
case WORKFLOW_STAGE_ID_EDITING:
|
||||
return __('submission.copyediting');
|
||||
case WORKFLOW_STAGE_ID_PRODUCTION:
|
||||
return __('submission.production');
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get decision message
|
||||
*
|
||||
* @param int $decision Decision::*...
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDecisionMessage($decision)
|
||||
{
|
||||
switch ($decision) {
|
||||
case Decision::ACCEPT:
|
||||
return __('editor.submission.decision.accept');
|
||||
case Decision::PENDING_REVISIONS:
|
||||
return __('editor.submission.decision.requestRevisions');
|
||||
case Decision::RESUBMIT:
|
||||
return __('editor.submission.decision.resubmit');
|
||||
case Decision::DECLINE:
|
||||
return __('editor.submission.decision.decline');
|
||||
case Decision::SEND_TO_PRODUCTION:
|
||||
return __('editor.submission.decision.sendToProduction');
|
||||
case Decision::EXTERNAL_REVIEW:
|
||||
return __('editor.submission.decision.sendExternalReview');
|
||||
case Decision::INITIAL_DECLINE:
|
||||
return __('editor.submission.decision.decline');
|
||||
case Decision::RECOMMEND_ACCEPT:
|
||||
return __('editor.submission.recommendation.display', ['recommendation' => __('editor.submission.decision.accept')]);
|
||||
case Decision::RECOMMEND_DECLINE:
|
||||
return __('editor.submission.recommendation.display', ['recommendation' => __('editor.submission.decision.decline')]);
|
||||
case Decision::RECOMMEND_PENDING_REVISIONS:
|
||||
return __('editor.submission.recommendation.display', ['recommendation' => __('editor.submission.decision.requestRevisions')]);
|
||||
case Decision::RECOMMEND_RESUBMIT:
|
||||
return __('editor.submission.recommendation.display', ['recommendation' => __('editor.submission.decision.resubmit')]);
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user