Skip to content

Commit

Permalink
pkp#9666 COUNTER R5 TSV reports
Browse files Browse the repository at this point in the history
  • Loading branch information
bozana committed Aug 26, 2024
1 parent 8af045a commit 0e39539
Show file tree
Hide file tree
Showing 9 changed files with 527 additions and 15 deletions.
103 changes: 103 additions & 0 deletions api/v1/stats/sushi/PKPStatsSushiController.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,15 @@
use Illuminate\Support\Facades\Route;
use PKP\core\PKPBaseController;
use PKP\core\PKPRequest;
use PKP\core\PKPRoutingProvider;
use PKP\security\authorization\ContextRequiredPolicy;
use PKP\security\authorization\PolicySet;
use PKP\security\authorization\RoleBasedHandlerOperationPolicy;
use PKP\security\authorization\UserRolesRequiredPolicy;
use PKP\security\Role;
use PKP\sushi\CounterR5Report;
use PKP\sushi\SushiException;
use PKP\validation\ValidatorFactory;

class PKPStatsSushiController extends PKPBaseController
{
Expand Down Expand Up @@ -257,19 +259,120 @@ public function getReportsPR1(Request $illuminateRequest): JsonResponse
return $this->getReportResponse(new PR_P1(), $illuminateRequest);
}

/** Validate user input for TSV reports */
protected function _validateUserInput(CounterR5Report $report, array $params): array
{
$request = $this->getRequest();
$context = $request->getContext();
$earliestDate = CounterR5Report::getEarliestDate();
$lastDate = CounterR5Report::getLastDate();
$submissionIds = Repo::submission()->getCollector()->filterByContextIds([$context->getId()])->getIds()->implode(',');

$rules = [
'begin_date' => [
'regex:/^\d{4}-\d{2}(-\d{2})?$/',
'after_or_equal:' . $earliestDate,
'before_or_equal:end_date',
],
'end_date' => [
'regex:/^\d{4}-\d{2}(-\d{2})?$/',
'before_or_equal:' . $lastDate,
'after_or_equal:begin_date',
],
'item_id' => [
// TO-ASK: shell this rather be just validation for positive integer?
'in:' . $submissionIds,
],
'yop' => [
'regex:/^\d{4}((\||-)\d{4})*$/',
],
];
$reportId = $report->getID();
if (in_array($reportId, ['PR', 'TR', 'IR'])) {
$rules['metric_type'] = ['required'];
}

$errors = [];
$validator = ValidatorFactory::make(
$params,
$rules,
[
'begin_date.regex' => __(
'manager.statistics.counterR5Report.settings.wrongDateFormat'
),
'end_date.regex' => __(
'manager.statistics.counterR5Report.settings.wrongDateFormat'
),
'begin_date.after_or_equal' => __(
'stats.dateRange.invalidStartDateMin'
),
'end_date.before_or_equal' => __(
'stats.dateRange.invalidEndDateMax'
),
'begin_date.before_or_equal' => __(
'stats.dateRange.invalidDateRange'
),
'end_date.after_or_equal' => __(
'stats.dateRange.invalidDateRange'
),
'item_id.*' => __(
'manager.statistics.counterR5Report.settings.wrongItemId'
),
'yop.regex' => __(
'manager.statistics.counterR5Report.settings.wrongYOPFormat'
),
]
);

if ($validator->fails()) {
$errors = $validator->errors()->getMessages();
}

return $errors;
}

/**
* Get the requested report
*/
protected function getReportResponse(CounterR5Report $report, Request $illuminateRequest): JsonResponse
{
$params = $illuminateRequest->query();
$responseTSV = str_contains($illuminateRequest->getHeaderLine('Accept'), PKPRoutingProvider::RESPONSE_TSV) ? true : false;

if ($responseTSV) {
$errors = $this->_validateUserInput($report, $params);
if (!empty($errors)) {
return response()->json($errors, 400);
}
}

try {
$report->processReportParams($this->getRequest(), $params);
} catch (SushiException $e) {
return response()->json($e->getResponseData(), $e->getHttpStatusCode());
}

if ($responseTSV) {
$reportHeader = $report->getTSVReportHeader();
$reportColumnNames = $report->getTSVColumnNames();
$reportItems = $report->getTSVReportItems();
// consider 3030 error (no usage available)
$key = array_search('3030', array_column($report->warnings, 'Code'));
if ($key !== false) {
$error = $report->warnings[$key]['Code'] . ':' . $report->warnings[$key]['Message'] . '(' . $report->warnings[$key]['Data'] . ')';
foreach ($reportHeader as &$headerRow) {
if (in_array('Exceptions', $headerRow)) {
$headerRow[1] =
$headerRow[1] == '' ?
$error :
$headerRow[1] . ';' . $error;
}
}
}
$report = array_merge($reportHeader, [['']], $reportColumnNames, $reportItems);
return response()->withCSV($report, [], count($reportItems), PKPRoutingProvider::RESPONSE_TSV);
}

$reportHeader = $report->getReportHeader();
$reportItems = $report->getReportItems();

Expand Down
66 changes: 66 additions & 0 deletions classes/components/forms/counter/PKPCounterReportForm.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?php
/**
* @file classes/components/form/counter/PKPCounterReportForm.php
*
* Copyright (c) 2024 Simon Fraser University
* Copyright (c) 2024 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class PKPCounterReportForm
*
* @ingroup classes_controllers_form
*
* @brief A form for setting a COUNTER R5 report
*/

namespace PKP\components\forms\counter;

use PKP\components\forms\FormComponent;

define('FORM_COUNTER', 'counter');

abstract class PKPCounterReportForm extends FormComponent
{
/** @copydoc FormComponent::$id */
public $id = FORM_COUNTER;

/** @copydoc FormComponent::$method */
public $method = 'GET';

/** Form fields for each COUNTER R5 report */
public $reportFields = [];

/** Set reportFields, that will contain form fields for each COUNTER R5 report */
abstract public function setReportFields(): void;

/**
* Constructor
*
* @param string $action URL to submit the form to
* @param array $locales Supported locales
*/
public function __construct(string $action, array $locales)
{
$this->action = $action;
$this->locales = $locales;

$this->addPage(['id' => 'default', 'submitButton' => ['label' => __('common.download')]]);
$this->addGroup(['id' => 'default', 'pageId' => 'default']);

$this->setReportFields();
}

public function getConfig()
{
$config = parent::getConfig();
$config['reportFields'] = array_map(function ($reportFields) {
return array_map(function ($reportField) {
$field = $this->getFieldConfig($reportField);
$field['groupId'] = 'default';
return $field;
}, $reportFields);
}, $this->reportFields);

return $config;
}
}
53 changes: 53 additions & 0 deletions classes/components/listPanels/PKPCounterReportsListPanel.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php
/**
* @file classes/components/listPanels/PKPCounterReportsListPanel.php
*
* Copyright (c) 2024 Simon Fraser University
* Copyright (c) 2024 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class PKPCounterReportsListPanel
*
* @ingroup classes_components_list
*
* @brief A ListPanel component for viewing, editing and downloading COUNTER R5 reports
*/

namespace PKP\components\listPanels;

use APP\components\forms\counter\CounterReportForm;
use PKP\sushi\CounterR5Report;

class PKPCounterReportsListPanel extends ListPanel
{
/** URL to the API endpoint where items can be retrieved */
public string $apiUrl = '';

/** Form for setting up and downloading a report*/
public ?CounterReportForm $form = null;

/** Query parameters to pass if this list executes GET requests */
public array $getParams = [];

/**
* @copydoc ListPanel::getConfig()
*/
public function getConfig(): array
{
$config = parent::getConfig();

$earliestDate = CounterR5Report::getEarliestDate();
$lastDate = CounterR5Report::getLastDate();

$config = array_merge(
$config,
[
'apiUrl' => $this->apiUrl,
'editCounterReportLabel' => __('manager.statistics.counterR5Report.settings'),
'form' => $this->form->getConfig(),
'usagePossible' => $lastDate > $earliestDate,
]
);
return $config;
}
}
10 changes: 8 additions & 2 deletions classes/services/queryBuilders/PKPStatsSushiQueryBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,10 @@ public function getSum(array $groupBy = []): Builder
$q->leftJoin('publications as p', function ($q) {
$q->on('p.submission_id', '=', 'm.submission_id')
->whereIn('p.publication_id', function ($q) {
$q->selectRaw('MIN(p2.publication_id)')->from('publications as p2')->where('p2.status', Submission::STATUS_PUBLISHED);
$q->selectRaw('MIN(p2.publication_id)')
->from('publications as p2')
->where('p2.status', Submission::STATUS_PUBLISHED)
->where('p2.submission_id', '=', DB::raw('m.submission_id'));
});
});
}
Expand Down Expand Up @@ -123,7 +126,10 @@ protected function _getObject(): Builder
$q->leftJoin('publications as p', function ($q) {
$q->on('p.submission_id', '=', 'm.submission_id')
->whereIn('p.publication_id', function ($q) {
$q->selectRaw('MIN(p2.publication_id)')->from('publications as p2')->where('p2.status', Submission::STATUS_PUBLISHED);
$q->selectRaw('MIN(p2.publication_id)')
->from('publications as p2')
->where('p2.status', Submission::STATUS_PUBLISHED)
->where('p2.submission_id', '=', DB::raw('m.submission_id'));
});
});
foreach ($this->yearsOfPublication as $yop) {
Expand Down
Loading

0 comments on commit 0e39539

Please sign in to comment.