Skip to content
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 34 additions & 6 deletions config/vufind/Folio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -236,18 +236,46 @@ in_transit[] = "Open - Awaiting delivery"
; the system will attempt to reconcile the global page size config there with the
; available options defined here.
page_size[] = 50
; Sort options
; Note: When choosing a sort, ensure that you select

; Sort options available in the "Checked Out Items" screen.
; These will be passed through to the FOLIO API as sortby parameters
; unless configured as a key in the vufind_sort option to be used
; to sort the items after they are returned from the API.
;
; This should be filled out as: sort[FOLIO_SORTBY_PARAM] or sort[VUFIND_SORT_KEY]
; with the value being the display label.
; For example to use FOLIO API sort, sort[itemId/sort.ascending] will pass
; the value 'itemId/sort.ascending' to the sortby parameter of the FOLIO API.
; To use the VuFind sort, sort[title] will check the vufind_sort[] for a key of title
; and sort by that option's value.
;
; Note: When choosing a FOLIO sort parameter, ensure that you choose
; field(s) that will return the records in a consistent order so that
; when paginating, will get all of the records in the same order for
; when paginating you will get all of the records in the same order for
; every page of results without possibly having duplicates appear or missing items.
; For example, using just dueDate will cause problems if there are more than
; your page_size of items due on the same day since the order is not guaranteed to be
; the same for non-unique values.
;sort[dueDate/sort.ascending itemId/sort.ascending] = sort_due_date_asc
;sort[dueDate/sort.descending itemId/sort.ascending] = sort_due_date_desc
sort[dueDate/sort.ascending itemId/sort.ascending] = sort_due_date_asc
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was it a conscious choice to uncomment the previously commented-out defaults?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. That was unintentional. I just pushed a commit to undo that change.

sort[dueDate/sort.descending itemId/sort.ascending] = sort_due_date_desc
;sort[title] = sort_title

; This optional setting sorts the results using a natural sort algorithm on the
; specified field of the VuFind checked out items output, which can be useful when FOLIO's
; native sort does not allow sorting on the desired field.
; If used, the key here should match the key used in the sort[] option and the value should
; be the field within the check out items output to sort by.
; For example, if you have sort[title] = sort_title then you could set vufind_sort[title] = "title"
; to create a sort option by the "title" field with a display label of sort_title.
; See the formatTransactionItem function for a full list of the available fields.
; Note that it will have performance implications since it will have to pull all loans,
; sort them, then slice the results down to the page size instead of
; only querying FOLIO for the current page size of results. For most users with loans
; spanning only one or two pages this will not have a significant impact.
;vufind_sort[title] = "title"

; Default sort (note: this must be defined in the sort[] options above to be applied!)
;default_sort="dueDate/sort.ascending itemId/sort.ascending"
default_sort="dueDate/sort.ascending itemId/sort.ascending"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same question here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above.


[CourseReserves]
; If set to true, the course number will be prefixed on the course name; if false,
Expand Down
129 changes: 90 additions & 39 deletions module/VuFind/src/VuFind/ILS/Driver/Folio.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
use VuFindHttp\HttpServiceAwareInterface as HttpServiceAwareInterface;

use function array_key_exists;
use function array_slice;
use function count;
use function in_array;
use function is_callable;
Expand Down Expand Up @@ -1144,19 +1145,32 @@ protected function formatHoldingItem(
* @return array
*/
protected function sortHoldings(array $holdings, string $sortField): array
{
return $this->sortArray($holdings, $sortField);
}

/**
* Given an array and a sort field, sort the array.
*
* @param array $data Array to sort
* @param string $sortField Sort field
*
* @return array
*/
protected function sortArray(array $data, string $sortField): array
Comment thread
demiankatz marked this conversation as resolved.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we do this anywhere else? If so, I wonder if a trait would make sense. But I didn't check, so maybe that's a premature suggestion. :-)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There isn't anywhere else in the code that currently does a strnatcasecmp. We have other sorting functions, but not ones that are sorting associative arrays based on a specific column (at least that I could find in my searching). I lean towards not making a trait until we have another use for it, but I could be convinced otherwise.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, no sense in overengineering this prematurely. I just thought there might be another similar function somewhere else. Thanks for confirming that there is not! :-)

{
usort(
$holdings,
$data,
function ($a, $b) use ($sortField) {
return strnatcasecmp($a[$sortField], $b[$sortField]);
}
);
// Renumber the re-sorted batch:
$nbCount = count($holdings);
$nbCount = count($data);
for ($nbIndex = 0; $nbIndex < $nbCount; $nbIndex++) {
$holdings[$nbIndex]['number'] = $nbIndex + 1;
$data[$nbIndex]['number'] = $nbIndex + 1;
}
return $holdings;
return $data;
}

/**
Expand Down Expand Up @@ -1829,53 +1843,90 @@ public function getMyTransactions($patron, $params = [])
$limit = $params['limit'] ?? 1000;
$offset = isset($params['page']) ? ($params['page'] - 1) * $limit : 0;

$requestedSort = $params['sort'] ?? '';
$vufindSortMap = $this->config['Loans']['vufind_sort'] ?? [];
$localSortField = $vufindSortMap[$requestedSort] ?? null;

$query = 'userId==' . $patron['id'] . ' and status.name==Open';
if (isset($params['sort'])) {
$query .= ' sortby ' . $this->escapeCql($params['sort']);
}
$resultPage = $this->getResultPage('/circulation/loans', compact('query'), $offset, $limit);
$transactions = [];
foreach ($resultPage->loans ?? [] as $trans) {
$dueStatus = false;
$date = $this->getDateTimeFromString($trans->dueDate);
$dueDateTimestamp = $date->getTimestamp();

$now = time();
if ($now > $dueDateTimestamp) {
$dueStatus = 'overdue';
} elseif ($now > $dueDateTimestamp - (1 * 24 * 60 * 60)) {
$dueStatus = 'due';
$count = null;

// Fetch data from FOLIO, only pass sort to API if it is NOT handled locally by VuFind
if ($localSortField) {
$rawItems = $this->getPagedResults('loans', '/circulation/loans', compact('query'));
} else {
if (!empty($requestedSort)) {
$query .= ' sortby ' . $this->escapeCql($requestedSort);
}
$transactions[] = [
'duedate' =>
$this->dateConverter->convertToDisplayDate(
'U',
$dueDateTimestamp
),
'dueTime' =>
$this->dateConverter->convertToDisplayTime(
'U',
$dueDateTimestamp
),
'dueStatus' => $dueStatus,
'id' => $this->getBibId($trans->item->instanceId),
'item_id' => $trans->item->id,
'barcode' => $trans->item->barcode,
'renew' => $trans->renewalCount ?? 0,
'renewable' => true,
'title' => $trans->item->title,
];
$result = $this->getResultPage('/circulation/loans', compact('query'), $offset, $limit);
$rawItems = $result->loans ?? [];
}
// If we have a full page or have applied an offset, we need to look up the total count of transactions:

// Format the transaction results
foreach ($rawItems as $trans) {
$transactions[] = $this->formatTransactionItem($trans);
}

// If we have a full page, have applied an offset, or are not using a vufind_sort option,
// we need to look up the total count of transactions:
$count = count($transactions);
if ($offset > 0 || $count >= $limit) {
if (!$localSortField && ($offset > 0 || $count >= $limit)) {
// We could use the count in the result page, but that may be an estimate;
// safer to do a separate lookup to be sure we have the right number!
$count = $this->getResultCount('/circulation/loans', compact('query'));
}

// Apply local sorted if requested
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Apply local sorted if requested
// Apply local sorting if requested

if ($localSortField) {
$transactions = $this->sortArray($transactions, $localSortField);
$transactions = array_slice($transactions, $offset, $limit);
}

return ['count' => $count, 'records' => $transactions];
}

/**
* Support method for getMyTransactions to parse a single
* transaction result.
*
* @param array $transaction An single transaction
* array from getMyTransactions
*
* @return string The FOLIO loan ID for this loan
*/
protected function formatTransactionItem($transaction)
{
$dueStatus = false;
$date = $this->getDateTimeFromString($transaction->dueDate);
$dueDateTimestamp = $date->getTimestamp();

$now = time();
if ($now > $dueDateTimestamp) {
$dueStatus = 'overdue';
} elseif ($now > $dueDateTimestamp - (1 * 24 * 60 * 60)) {
$dueStatus = 'due';
}
return [
'duedate' =>
$this->dateConverter->convertToDisplayDate(
'U',
$dueDateTimestamp
),
'dueTime' =>
$this->dateConverter->convertToDisplayTime(
'U',
$dueDateTimestamp
),
'dueStatus' => $dueStatus,
'id' => $this->getBibId($transaction->item->instanceId),
'item_id' => $transaction->item->id,
'barcode' => $transaction->item->barcode,
'renew' => $transaction->renewalCount ?? 0,
'renewable' => true,
'title' => $transaction->item->title,
];
}

/**
* Get FOLIO loan IDs for use in renewMyItems.
*
Expand Down
Loading