Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FINNA-2662] Adjust loan history export to use paging #3076

Open
wants to merge 7 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
4 changes: 4 additions & 0 deletions local/config/finna/config.ini.sample
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,10 @@ library_cards = true
; memory problems for users with a large number of historic loans). Default = 50
;historic_loan_page_size = 50

; Limit for how many historic transactions to fetch at most from ILS
; when downloading loan history
loan_history_download_batch_limit = 1000

; Whether to display the item barcode for each loan. Default is false.
display_checked_out_item_barcode = true

Expand Down
11 changes: 7 additions & 4 deletions local/languages/finna/en-gb.ini
Original file line number Diff line number Diff line change
Expand Up @@ -599,10 +599,13 @@ list_order_saved = "Sort order saved"
list-tags-info = "Add a new keyword"
Loading = "Loading"
Loan Details = "Loan Details"
loan_history_download = "Export all"
loan_history_download_csv = "Export CSV"
loan_history_download_ods = "Export OpenOffice (ods)"
loan_history_download_xlsx = "Export Excel (xlsx)"
loan_history_download = "Download loan history"
loan_history_download_csv = "CSV"
loan_history_download_ods = "OpenOffice (ods)"
loan_history_download_xlsx = "Excel (xlsx)"
loan_history_info = "Pages downloaded at most %%total%%"
loan_history_pages = "Pages available to download: %%total%%"
loan_history_choose_file_format = "Choose file format"
loan_history_purge = "Purge History"
loan_history_purge_prompt_html = "Are you sure you will purge the loan history? Cleared loan history cannot be retrieved anymore."
loan_history_purge_selected = "Purge Selected"
Expand Down
11 changes: 7 additions & 4 deletions local/languages/finna/fi.ini
Original file line number Diff line number Diff line change
Expand Up @@ -589,10 +589,13 @@ list_order_saved = "Järjestys tallennettu"
list-tags-info = "Lisää uusi avainsana"
Loading = "Ladataan"
Loan Details = "Lainan tiedot"
loan_history_download = "Vie kaikki"
loan_history_download_csv = "Vie CSV"
loan_history_download_ods = "Vie OpenOffice (ods)"
loan_history_download_xlsx = "Vie Excel (xlsx)"
loan_history_download = "Lataa lainaushistoria"
loan_history_download_csv = "CSV"
loan_history_download_ods = "OpenOffice (ods)"
loan_history_download_xlsx = "Excel (xlsx)"
loan_history_info = "Sivuja ladataan korkeintaan %%total%%"
loan_history_pages = "Sivuja ladattavana: %%total%%"
loan_history_choose_file_format = "Valitse tiedostomuoto"
loan_history_purge = "Tyhjennä historia"
loan_history_purge_prompt_html = "Oletko varma, että haluat tyhjentää lainaushistoriasi? Tyhjennettyä historiaa ei saa takaisin."
loan_history_purge_selected = "Poista valitut"
Expand Down
11 changes: 7 additions & 4 deletions local/languages/finna/sv.ini
Original file line number Diff line number Diff line change
Expand Up @@ -587,10 +587,13 @@ list_order_saved = "Sortering sparad"
list-tags-info = "Lägg till nytt nyckelord"
Loading = "Laddar"
Loan Details = "Information om lånet"
loan_history_download = "Exportera alla"
loan_history_download_csv = "Exportera CSV"
loan_history_download_ods = "Exportera OpenOffice (ods)"
loan_history_download_xlsx = "Exportera Excel (xlsx)"
loan_history_download = "Ladda lånehistorik"
loan_history_download_csv = "CSV"
loan_history_download_ods = "OpenOffice (ods)"
loan_history_download_xlsx = "Excel (xlsx)"
loan_history_info = "Sidorna laddas högst %%total%%"
loan_history_pages = "Sidorna tillgängliga för nedladdning: %%total%%"
loan_history_choose_file_format = "Välja filformat"
loan_history_purge = "Rensa historiken"
loan_history_purge_prompt_html = "Är du säker på att du vill rensa din utlåningshistorik? Raderad historik kan inte återskapas."
loan_history_purge_selected = "Radera valda"
Expand Down
225 changes: 128 additions & 97 deletions module/Finna/src/Finna/Controller/MyResearchController.php
Original file line number Diff line number Diff line change
Expand Up @@ -1347,116 +1347,147 @@ public function downloadLoanHistoryAction()
'getMyTransactionHistory',
$patron
);

if (false === $functionConfig) {
$this->flashMessenger()->addErrorMessage('ils_action_unavailable');
return $this->redirect()->toRoute('checkouts-history');
}
$fileFormat = $this->params()->fromQuery('format', '');
if (!in_array($fileFormat, ['ods', 'csv', 'xlsx'])) {
throw new \Exception('Invalid parameters.');

$allowedFileFormats = ['csv', 'ods', 'xlsx'];
$historyResult = $this->forwardTo('checkouts', 'history');
if (!isset($historyResult->transactions)) {
return $historyResult;
}
$batchLimit = $this->getConfig()->Catalog->loan_history_download_batch_limit ?? 1000;
$pagesTotal = $historyResult->paginator ? $historyResult->paginator->count() : 1;
$pagesDownloadCounter = 1;
// Calculate how many times required to fetch from ILS to achieve the $batchLimit
if ($pagesTotal > 1) {
$pagesDownloadCounter = ceil($batchLimit / $historyResult->paginator->getItemCountPerPage());
}
if (!$this->formWasSubmitted('submitLoanHistoryRequest')) {
$view = $this->createViewModel([
'fileFormats' => $allowedFileFormats,
'params' => $historyResult->params,
'pagesTotal' => $pagesTotal,
'pagesDownloadCounter' => $pagesDownloadCounter,
]);
$view->setTemplate('checkouts/downloadhistory.phtml');
return $view;
}
$request = $this->getRequest();
if (!$request->isPost()) {
throw new \Exception('Invalid method.');
}

$recordLoader = $this->serviceLocator->get(\VuFind\Record\Loader::class);
$page = 1;
try {
$tmp = fopen('php://temp/maxmemory:' . (5 * 1024 * 1024), 'r+');
$header = [
$this->translate('Title'),
$this->translate('Format'),
$this->translate('Author'),
$this->translate('Publication Year'),
$this->translate('Institution'),
$this->translate('Borrowing Location'),
$this->translate('Checkout Date'),
$this->translate('Return Date'),
$this->translate('Due Date'),
];
$spreadsheet = new Spreadsheet();
$worksheet = $spreadsheet->getActiveSheet();
$worksheet->fromArray($header);
if ('xlsx' === $fileFormat) {
Cell::setValueBinder(new AdvancedValueBinder());
}
do {
// Try to use large page size, but take ILS limits into account
$pageOptions = $this->getPaginationHelper()
->getOptions($page, null, 1000, $functionConfig);
$result = $catalog
->getMyTransactionHistory($patron, $pageOptions['ilsParams']);
// Do CSRF check
$csrf = $this->serviceLocator->get(\VuFind\Validator\CsrfInterface::class);
if (!$csrf->isValid($request->getPost('csrf'))) {
throw new \VuFind\Exception\BadRequest('error_inconsistent_parameters');
}
$fileFormat = $request->getPost('history_file_format', '');
if (!in_array($fileFormat, $allowedFileFormats)) {
throw new \Exception('Invalid format.');
}

if (isset($result['success']) && !$result['success']) {
$this->flashMessenger()->addErrorMessage($result['status']);
return $this->redirect()->toRoute('checkouts-history');
}
$startPage = (int)$request->getPost('startPage', 1);
$lastPage = min($startPage + $pagesDownloadCounter, $pagesTotal);

$ids = [];
foreach ($result['transactions'] as $current) {
$id = $current['id'] ?? '';
$source = $current['source'] ?? DEFAULT_SEARCH_BACKEND;
$ids[] = compact('id', 'source');
}
$records = $recordLoader->loadBatch($ids, true);
foreach ($result['transactions'] as $i => $current) {
$driver = $records[$i];
$format = $driver->getFormats();
$format = end($format);
$author = $driver->tryMethod('getNonPresenterAuthors');

$loan = [];
$loan[] = $current['title'] ?? $driver->getTitle() ?? '';
$loan[] = $this->translate($format);
$loan[] = $author[0]['name'] ?? '';
$loan[] = $current['publication_year'] ?? '';
$loan[] = empty($current['institution_name'])
? ''
: $this->translateWithPrefix('location_', $current['institution_name']);
$loan[] = empty($current['borrowingLocation'])
? ''
: $this->translateWithPrefix('location_', $current['borrowingLocation']);
$loan[] = $current['checkoutDate'] ?? '';
$loan[] = $current['returnDate'] ?? '';
$loan[] = $current['dueDate'] ?? '';

$nextRow = $worksheet->getHighestRow() + 1;
$worksheet->fromArray($loan, null, 'A' . (string)$nextRow);
}
$recordLoader = $this->serviceLocator->get(\VuFind\Record\Loader::class);
$tmp = fopen('php://temp/maxmemory:' . (5 * 1024 * 1024), 'r+');

$pageEnd = $pageOptions['ilsPaging']
? ceil($result['count'] / $pageOptions['limit'])
: 1;
$page++;
} while ($page <= $pageEnd);
if ('xlsx' === $fileFormat) {
$worksheet->getStyle('G2:I' . $worksheet->getHighestRow())
->getNumberFormat()
->setFormatCode('dd.mm.yyyy');
foreach (['G', 'H', 'I'] as $col) {
$worksheet->getColumnDimension($col)->setAutoSize(true);
}
$transactions = [];
for ($i = $startPage; $i <= $lastPage; $i++) {
$result = $catalog->getMyTransactionHistory($patron, $historyResult->params);
if (isset($result['success']) && !$result['success']) {
$this->flashMessenger()->addErrorMessage($result['status']);
return $this->redirect()->toRoute('checkouts-history');
}
$response = $this->getResponse();
$response->getHeaders()
->addHeaderLine(
'Content-Type',
$this->exportFormats[$fileFormat]['mediaType']
);
$writer = new $this->exportFormats[$fileFormat]['writer']($spreadsheet);
$writer->save($tmp);

$response->getHeaders()
->addHeaderLine(
'Content-Disposition',
'attachment; filename="finna-loan-history.' . $fileFormat . '"'
);

rewind($tmp);
// Break if no transactions found
if (empty($result['transactions'])) {
break;
}
$transactions = array_merge($transactions, $result['transactions']);
}
$ids = [];
foreach ($transactions as $current) {
$id = $current['id'] ?? '';
$source = $current['source'] ?? DEFAULT_SEARCH_BACKEND;
$ids[] = compact('id', 'source');
}
$records = $recordLoader->loadBatch($ids, true);

$header = [
$this->translate('Title'),
$this->translate('Format'),
$this->translate('Author'),
$this->translate('Publication Year'),
$this->translate('Institution'),
$this->translate('Borrowing Location'),
$this->translate('Checkout Date'),
$this->translate('Return Date'),
$this->translate('Due Date'),
];

$response->setContent(stream_get_contents($tmp));
} catch (\Exception $e) {
$this->flashMessenger()->addErrorMessage('An error has occurred');
return $this->redirect()->toRoute('checkouts-history');
$spreadsheet = new Spreadsheet();
$worksheet = $spreadsheet->getActiveSheet();
$worksheet->fromArray($header);

if ('xlsx' === $fileFormat) {
Cell::setValueBinder(new AdvancedValueBinder());
}

foreach ($transactions as $i => $current) {
$driver = $records[$i];
$format = $driver->getFormats();
$format = end($format);
$author = $driver->tryMethod('getNonPresenterAuthors');

$loan = [];
$loan[] = $current['title'] ?? $driver->getTitle() ?? '';
$loan[] = $this->translate($format);
$loan[] = $author[0]['name'] ?? '';
$loan[] = $current['publication_year'] ?? '';
$loan[] = empty($current['institution_name'])
? ''
: $this->translateWithPrefix('location_', $current['institution_name']);
$loan[] = empty($current['borrowingLocation'])
? ''
: $this->translateWithPrefix('location_', $current['borrowingLocation']);
$loan[] = $current['checkoutDate'] ?? '';
$loan[] = $current['returnDate'] ?? '';
$loan[] = $current['dueDate'] ?? '';

$nextRow = $worksheet->getHighestRow() + 1;
$worksheet->fromArray($loan, null, 'A' . (string)$nextRow);
}
if ('xlsx' === $fileFormat) {
$worksheet->getStyle('G2:I' . $worksheet->getHighestRow())
->getNumberFormat()
->setFormatCode('dd.mm.yyyy');
foreach (['G', 'H', 'I'] as $col) {
$worksheet->getColumnDimension($col)->setAutoSize(true);
}
}
$response = $this->getResponse();
$response->getHeaders()
->addHeaderLine(
'Content-Type',
$this->exportFormats[$fileFormat]['mediaType']
);
$writer = new $this->exportFormats[$fileFormat]['writer']($spreadsheet);
$writer->save($tmp);
$fileName = implode('-', ['finna-loan-history-pages', $startPage, $lastPage]);
$fileName .= ".$fileFormat";
$response->getHeaders()
->addHeaderLine(
'Content-Disposition',
'attachment; filename="' . $fileName . '"'
);

rewind($tmp);

$response->setContent(stream_get_contents($tmp));
return $response;
}

Expand Down
34 changes: 34 additions & 0 deletions themes/finna2/templates/checkouts/downloadhistory.phtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<!-- START of: finna - myresearch/downloadhistory.phtml -->
<?php
// Set up page title:
$this->headTitle($this->translate('loan_history_download'));

// Set up breadcrumbs:
$this->layout()->breadcrumbs = '<li><a href="' . $this->url('myresearch-home') . '">' . $this->transEsc('Your Account') . '</a></li> <li class="active">' . $this->transEsc('Loan History') . '</li>';
?>
<?=$this->context($this)->renderInContext('myresearch/menu.phtml', ['active' => 'historicLoans']); ?>

<div class="<?=$this->layoutClass('mainbody-myresearch')?>">
<h2><?=$this->transEsc('loan_history_download')?></h2>
<form name="download-loan-history-form" action="<?=$this->url('myresearch-downloadloanhistory')?>" method="POST" enctype="multipart/form-data">
<input type="hidden" name="csrf" value="<?=$this->escapeHtmlAttr($this->auth()->getManager()->getCsrfHash())?>">
<div class="alert alert-info">
<span><?=$this->transEsc('loan_history_info', ['%%total%%' => $this->pagesDownloadCounter])?></span>
</div>
<span><?=$this->transEsc('loan_history_pages', ['%%total%%' => $this->pagesTotal])?></span>
<div class="form-group">
<label class="control-label" for="history_start_index"><?=$this->transEsc('Start Page');?>:</label>
<input class="form-control" id="history_start_index" name="startPage" type="number" value="1" min="1" max="<?=$this->escapeHtmlAttr($this->pagesTotal)?>">
</div>
<div class="form-group">
<label class="control-label" for="history_file_new"><?=$this->transEsc('loan_history_choose_file_format')?>:</label>
<select class="form-control" name="history_file_format" id="history_file_new">
<?php foreach ($this->fileFormats as $format): ?>
<option value="<?=$this->escapeHtmlAttr($format)?>"><?=$this->transEscWithPrefix('loan_history_download_', $format);?></option>
<?php endforeach; ?>
</select>
</div>
<button data-lightbox-ignore class="btn btn-primary" type="submit" name="submitLoanHistoryRequest" value="<?=$this->transEscAttr('Submit')?>"><?=$this->transEsc('Submit');?></button>
</form>
</div>
<!-- END of: finna - myresearch/downloadhistory.phtml -->
19 changes: 4 additions & 15 deletions themes/finna2/templates/checkouts/history.phtml
Original file line number Diff line number Diff line change
Expand Up @@ -89,21 +89,10 @@
</div>
<?php if (!empty($this->transactions)): ?>
<div class="btn-group">
<div class="dropdown loan-history-download">
<button class="btn btn-primary btn-finna-toolbar dropdown-toggle download" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<?=$this->transEsc('loan_history_download')?> <span class="caret"></span>
</button>
<ul class="dropdown-menu dropdown-menu-<?php if ($purgeSelectedAllowed): ?>right<?php else: ?>left<?php endif;?> download">
<li>
<a data-lightbox href="<?=$this->url('myresearch-savehistoricloans') ?>"><?=$this->transEsc('loan_history_save')?></a>
</li>
<?php foreach (['csv', 'ods', 'xlsx'] as $format): ?>
<li>
<a href="<?=$this->url('myresearch-downloadloanhistory', [], ['query' => ['format' => $format]])?>"><?=$this->transEsc('loan_history_download_' . $format)?></a>
</li>
<?php endforeach; ?>
</ul>
</div>
<a data-lightbox class="btn btn-primary btn-finna-toolbar" role="button" href="<?=$this->url('myresearch-savehistoricloans') ?>"><?=$this->transEsc('loan_history_save')?></a>
</div>
<div class="btn-group">
<a class="btn btn-primary btn-finna-toolbar" role="button" href="<?=$this->url('myresearch-downloadloanhistory')?>"><?=$this->transEsc('loan_history_download')?></a>
</div>
<?php endif; ?>
</div>
Expand Down