Open Journal Systems  3.3.0
PKPStatsEditorialService.inc.php
1 <?php
2 
17 namespace PKP\Services;
18 
20 
27  public function getOverview($args = []) {
28  import('classes.workflow.EditorDecisionActionsManager');
29  import('lib.pkp.classes.submission.PKPSubmission');
30  \AppLocale::requireComponents(LOCALE_COMPONENT_PKP_MANAGER, LOCALE_COMPONENT_APP_MANAGER);
31 
32  $received = $this->countSubmissionsReceived($args);
33  $accepted = $this->countByDecisions(SUBMISSION_EDITOR_DECISION_ACCEPT, $args);
34  $declinedDesk = $this->countByDecisions(SUBMISSION_EDITOR_DECISION_INITIAL_DECLINE, $args);
35  $declinedReview = $this->countByDecisions(SUBMISSION_EDITOR_DECISION_DECLINE, $args);
36  $declined = $declinedDesk + $declinedReview;
37 
38  // Calculate the acceptance/decline rates
39  if (!$received) {
40  // Never divide by 0
41  $acceptanceRate = 0;
42  $declineRate = 0;
43  $declinedDeskRate = 0;
44  $declinedReviewRate = 0;
45  } elseif (empty($args['dateStart']) && empty($args['dateEnd'])) {
46  $acceptanceRate = $accepted / $received;
47  $declineRate = $declined / $received;
48  $declinedDeskRate = $declinedDesk / $received;
49  $declinedReviewRate = $declinedReview / $received;
50  } else {
51  // To calculate the acceptance/decline rates within a date range
52  // we must collect the total number of all submissions made within
53  // that date range which have received a decision. The acceptance
54  // rate is the number of submissions made within the date range
55  // that were accepted divided by the number of submissions made
56  // within the date range that were accepted or declined. This
57  // excludes submissions that were made within the date range but
58  // have not yet been accepted or declined.
59  $acceptedForSubmissionDate = $this->countByDecisionsForSubmittedDate(SUBMISSION_EDITOR_DECISION_ACCEPT, $args);
60  $declinedDeskForSubmissionDate = $this->countByDecisionsForSubmittedDate(SUBMISSION_EDITOR_DECISION_INITIAL_DECLINE, $args);
61  $declinedReviewForSubmissionDate = $this->countByDecisionsForSubmittedDate(SUBMISSION_EDITOR_DECISION_DECLINE, $args);
62  $totalDecidedForSubmissionDate = $acceptedForSubmissionDate + $declinedDeskForSubmissionDate + $declinedReviewForSubmissionDate;
63 
64  // Never divide by 0
65  if (!$totalDecidedForSubmissionDate) {
66  $acceptanceRate = 0;
67  $declineRate = 0;
68  $declinedDeskRate = 0;
69  $declinedReviewRate = 0;
70  } else {
71  $acceptanceRate = $acceptedForSubmissionDate / $totalDecidedForSubmissionDate;
72  $declineRate = ($declinedDeskForSubmissionDate + $declinedReviewForSubmissionDate) / $totalDecidedForSubmissionDate;
73  $declinedDeskRate = $declinedDeskForSubmissionDate / $totalDecidedForSubmissionDate;
74  $declinedReviewRate = $declinedReviewForSubmissionDate / $totalDecidedForSubmissionDate;
75  }
76  }
77 
78  // Calculate the number of days it took for most submissions to
79  // receive decisions
80  $firstDecisionDays = $this->getDaysToDecisions([], $args);
81  $acceptDecisionDays = $this->getDaysToDecisions([SUBMISSION_EDITOR_DECISION_SEND_TO_PRODUCTION, SUBMISSION_EDITOR_DECISION_ACCEPT], $args);
82  $declineDecisionDays = $this->getDaysToDecisions([SUBMISSION_EDITOR_DECISION_DECLINE, SUBMISSION_EDITOR_DECISION_INITIAL_DECLINE], $args);
83  $firstDecisionDaysRate = empty($firstDecisionDays) ? 0 : $this->calculateDaysToDecisionRate($firstDecisionDays, 0.8);
84  $acceptDecisionDaysRate = empty($acceptDecisionDays) ? 0 : $this->calculateDaysToDecisionRate($acceptDecisionDays, 0.8);
85  $declineDecisionDaysRate = empty($declineDecisionDays) ? 0 : $this->calculateDaysToDecisionRate($declineDecisionDays, 0.8);
86 
87  $overview = [
88  [
89  'key' => 'submissionsReceived',
90  'name' => 'stats.name.submissionsReceived',
91  'value' => $received,
92  ],
93  [
94  'key' => 'submissionsAccepted',
95  'name' => 'stats.name.submissionsAccepted',
96  'value' => $accepted,
97  ],
98  [
99  'key' => 'submissionsDeclined',
100  'name' => 'stats.name.submissionsDeclined',
101  'value' => $declined,
102  ],
103  [
104  'key' => 'submissionsDeclinedDeskReject',
105  'name' => 'stats.name.submissionsDeclinedDeskReject',
106  'value' => $declinedDesk,
107  ],
108  [
109  'key' => 'submissionsDeclinedPostReview',
110  'name' => 'stats.name.submissionsDeclinedPostReview',
111  'value' => $declinedReview,
112  ],
113  [
114  'key' => 'submissionsPublished',
115  'name' => 'stats.name.submissionsPublished',
116  'value' => $this->countSubmissionsPublished($args),
117  ],
118  [
119  'key' => 'daysToDecision',
120  'name' => 'stats.name.daysToDecision',
121  'value' => $firstDecisionDaysRate,
122  ],
123  [
124  'key' => 'daysToAccept',
125  'name' => 'stats.name.daysToAccept',
126  'value' => $acceptDecisionDaysRate,
127  ],
128  [
129  'key' => 'daysToReject',
130  'name' => 'stats.name.daysToReject',
131  'value' => $declineDecisionDaysRate,
132  ],
133  [
134  'key' => 'acceptanceRate',
135  'name' => 'stats.name.acceptanceRate',
136  'value' => round($acceptanceRate, 2),
137  ],
138  [
139  'key' => 'declineRate',
140  'name' => 'stats.name.declineRate',
141  'value' => round($declineRate, 2),
142  ],
143  [
144  'key' => 'declinedDeskRate',
145  'name' => 'stats.name.declinedDeskRate',
146  'value' => round($declinedDeskRate, 2),
147  ],
148  [
149  'key' => 'declinedReviewRate',
150  'name' => 'stats.name.declinedReviewRate',
151  'value' => round($declinedReviewRate, 2),
152  ],
153  ];
154 
155  \HookRegistry::call('EditorialStats::overview', [&$overview, $args]);
156 
157  return $overview;
158  }
159 
177  public function getAverages($args = []) {
178  import('classes.workflow.EditorDecisionActionsManager');
179  import('lib.pkp.classes.submission.PKPSubmission');
180 
181  unset($args['dateStart']);
182  unset($args['dateEnd']);
183 
184  // Submissions received
185  $received = -1;
186  $receivedDates = $this->getQueryBuilder($args)->getSubmissionsReceivedDates();
187  if (empty($receivedDates[0])) {
188  $received = 0;
189  } else {
190  $yearStart = ((int) substr($receivedDates[0], 0, 4)) + 1;
191  $yearEnd = (int) substr($receivedDates[1], 0, 4);
192  if ($yearEnd >= date('Y')) {
193  $yearEnd--;
194  }
195  $years = ($yearEnd - $yearStart) + 1;
196  if ($years) {
197  $argsReceived = array_merge(
198  $args,
199  [
200  'dateStart' => sprintf('%s-01-01', $yearStart),
201  'dateEnd' => sprintf('%s-12-31', $yearEnd),
202  ]
203  );
204  $received = round($this->countSubmissionsReceived($argsReceived) / $years);
205  }
206  }
207 
208  // Editorial decisions (accepted and declined)
209  $decisionsList = [
210  'submissionsAccepted' => [SUBMISSION_EDITOR_DECISION_ACCEPT],
211  'submissionsDeclined' => [SUBMISSION_EDITOR_DECISION_INITIAL_DECLINE, SUBMISSION_EDITOR_DECISION_DECLINE],
212  'submissionsDeclinedDeskReject' => [SUBMISSION_EDITOR_DECISION_INITIAL_DECLINE],
213  'submissionsDeclinedPostReview' => [SUBMISSION_EDITOR_DECISION_DECLINE],
214  ];
215  $yearlyDecisions = [];
216  foreach ($decisionsList as $key => $decisions) {
217  $yearly = -1;
218  $dates = $this->getQueryBuilder($args)->getDecisionsDates($decisions);
219  if (empty($dates[0])) {
220  $yearly = 0;
221  } else {
222  $yearStart = ((int) substr($dates[0], 0, 4)) + 1;
223  $yearEnd = (int) substr($dates[1], 0, 4);
224  if ($yearEnd >= date('Y')) {
225  $yearEnd--;
226  }
227  $years = ($yearEnd - $yearStart) + 1;
228  if ($years) {
229  $argsYearly = array_merge(
230  $args,
231  [
232  'dateStart' => sprintf('%s-01-01', $yearStart),
233  'dateEnd' => sprintf('%s-12-31', $yearEnd),
234  ]
235  );
236  $yearly = round($this->countByDecisions($decisions, $argsYearly) / $years);
237  }
238  }
239  $yearlyDecisions[$key] = $yearly;
240  }
241 
242  // Submissions published
243  $published = -1;
244  $publishedDates = $this->getQueryBuilder($args)->getPublishedDates();
245  if (empty($publishedDates[0])) {
246  $published = 0;
247  } else {
248  $yearStart = ((int) substr($publishedDates[0], 0, 4)) + 1;
249  $yearEnd = (int) substr($publishedDates[1], 0, 4);
250  if ($yearEnd >= date('Y')) {
251  $yearEnd--;
252  }
253  $years = ($yearEnd - $yearStart) + 1;
254  if ($years) {
255  $argsPublished = array_merge(
256  $args,
257  [
258  'dateStart' => sprintf('%s-01-01', $yearStart),
259  'dateEnd' => sprintf('%s-12-31', $yearEnd),
260  ]
261  );
262  $published = round($this->countSubmissionsPublished($argsPublished) / $years);
263  }
264  }
265 
266  $averages = array_merge(
267  ['submissionsReceived' => $received],
268  $yearlyDecisions,
269  ['submissionsPublished' => $published]
270  );
271 
272  \HookRegistry::call('EditorialStats::averages', [&$averages, $args]);
273 
274  return $averages;
275  }
276 
286  public function countSubmissionsReceived($args = []) {
287  return $this->getQueryBuilder($args)->countSubmissionsReceived();
288  }
289 
290 
300  public function countSubmissionsPublished($args = []) {
301  return $this->getQueryBuilder($args)->countPublished();
302  }
303 
314  public function countByDecisions($decisions, $args = []) {
315  return $this->getQueryBuilder($args)->countByDecisions((array) $decisions);
316  }
317 
329  public function countByDecisionsForSubmittedDate($decisions, $args = []) {
330  return $this->getQueryBuilder($args)->countByDecisions((array) $decisions, true);
331  }
332 
343  public function countByStatus($statuses, $args = []) {
344  return $this->getQueryBuilder($args)->countByStatus((array) $statuses);
345  }
346 
357  public function countActiveByStages($stages, $args = []) {
358  return $this->getQueryBuilder($args)->countActiveByStages((array) $stages);
359  }
360 
373  public function getDaysToDecisions($decisions, $args = []) {
374  return $this->getQueryBuilder($args)->getDaysToDecisions((array) $decisions);
375  }
376 
388  public function getAverageDaysToDecisions($decisions, $args = []) {
389  return ceil($this->getQueryBuilder($args)->getAverageDaysToDecisions((array) $decisions));
390  }
391 
408  public function calculateDaysToDecisionRate($days, $percentage) {
409  sort($days);
410  $arrayPart = array_slice($days, 0, ceil(count($days) * $percentage));
411  return end($arrayPart) ?? 0;
412  }
413 
425  protected function getQueryBuilder($args = []) {
426  $qb = new \APP\Services\QueryBuilders\StatsEditorialQueryBuilder();
427 
428  if (!empty($args['dateStart'])) {
429  $qb->after($args['dateStart']);
430  }
431  if (!empty($args['dateEnd'])) {
432  $qb->before($args['dateEnd']);
433  }
434  if (!empty($args['contextIds'])) {
435  $qb->filterByContexts($args['contextIds']);
436  }
437 
438  \HookRegistry::call('Stats::editorial::queryBuilder', array($qb, $args));
439 
440  return $qb;
441  }
442 }
PKP\Services\PKPStatsEditorialService\countSubmissionsPublished
countSubmissionsPublished($args=[])
Definition: PKPStatsEditorialService.inc.php:300
PKP\Services
AppLocale\requireComponents
static requireComponents()
Definition: env1/MockAppLocale.inc.php:56
PKP\Services\PKPStatsEditorialService\getOverview
getOverview($args=[])
Definition: PKPStatsEditorialService.inc.php:27
PKP\Services\PKPStatsEditorialService\countByDecisions
countByDecisions($decisions, $args=[])
Definition: PKPStatsEditorialService.inc.php:314
PKP\Services\PKPStatsEditorialService\countByStatus
countByStatus($statuses, $args=[])
Definition: PKPStatsEditorialService.inc.php:343
PKP\Services\PKPStatsEditorialService
Definition: PKPStatsEditorialService.inc.php:19
PKP\Services\PKPStatsEditorialService\countActiveByStages
countActiveByStages($stages, $args=[])
Definition: PKPStatsEditorialService.inc.php:357
PKP\Services\PKPStatsEditorialService\getAverageDaysToDecisions
getAverageDaysToDecisions($decisions, $args=[])
Definition: PKPStatsEditorialService.inc.php:388
PKP\Services\PKPStatsEditorialService\countSubmissionsReceived
countSubmissionsReceived($args=[])
Definition: PKPStatsEditorialService.inc.php:286
PKP\Services\PKPStatsEditorialService\getAverages
getAverages($args=[])
Definition: PKPStatsEditorialService.inc.php:177
PKP\Services\PKPStatsEditorialService\calculateDaysToDecisionRate
calculateDaysToDecisionRate($days, $percentage)
Definition: PKPStatsEditorialService.inc.php:408
PKP\Services\PKPStatsEditorialService\countByDecisionsForSubmittedDate
countByDecisionsForSubmittedDate($decisions, $args=[])
Definition: PKPStatsEditorialService.inc.php:329
HookRegistry\call
static call($hookName, $args=null)
Definition: HookRegistry.inc.php:86
PKP\Services\PKPStatsEditorialService\getDaysToDecisions
getDaysToDecisions($decisions, $args=[])
Definition: PKPStatsEditorialService.inc.php:373
PKP\Services\PKPStatsEditorialService\getQueryBuilder
getQueryBuilder($args=[])
Definition: PKPStatsEditorialService.inc.php:425