Skip to content

Commit

Permalink
Use server-side paging in aips page. (#209)
Browse files Browse the repository at this point in the history
To avoid slow page responses when there are a large amount of AIPs use
server-side paging to limit the number of AIP HTML sent to the browser.
  • Loading branch information
mcantelon committed Sep 30, 2023
1 parent 40b01a3 commit fc3fe8d
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 21 deletions.
19 changes: 19 additions & 0 deletions AIPscan/Reporter/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import csv
from datetime import timedelta
from io import StringIO
from urllib.parse import urlencode

from flask import make_response
from natsort import natsorted
Expand Down Expand Up @@ -145,3 +146,21 @@ def get_premis_xml_lines(file_object):
premis_xml_lines = file_object.premis_object.split("\n")

return premis_xml_lines


def calculate_paging_window(page, pagination):
first_item = ((page - 1) * pagination.per_page) + 1
last_item = page * pagination.per_page

if last_item > pagination.total:
last_item = pagination.total

return first_item, last_item


def urlencode_without_none_values(values):
for key in values:
if values[key] is None:
values[key] = ""

return urlencode(values)
1 change: 1 addition & 0 deletions AIPscan/Reporter/request_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
PUID = "puid"
STORAGE_LOCATION_ID = "storage_location"
STORAGE_SERVICE_ID = "amss_id"
PAGE = "page"

START_DATE = "start_date"
END_DATE = "end_date"
53 changes: 37 additions & 16 deletions AIPscan/Reporter/templates/aips.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

{% block content %}

<div class="alert alert-header"><strong>AIPs ({{ aips|length }})</strong></div>
<div class="alert alert-header"><strong>AIPs ({{ pager.items|length }})</strong></div>

{% if storage_services %}

Expand Down Expand Up @@ -42,9 +42,9 @@
</div>
</div>

{% if aips %}
{% if pager.items %}

<table id="aiptable" class="table table-striped table-bordered">
<table id="aipstable" class="table table-striped table-bordered">
<thead>
<tr>
<th><strong>Transfer name</strong></th>
Expand All @@ -54,19 +54,17 @@
<th><strong>Preservation copies</strong></th>
<th><strong>Action</strong></th>
</tr>
</thead>
{% if aips %}
{% for aip in aips %}
<tr>
<td>{{ aip.transfer_name }}</td>
<td>{{ aip.uuid }}</td>
<td>{{ aip.create_date }}</td>
<td>{{ aip.original_file_count }}</td>
<td>{{ aip.preservation_file_count }}</td>
<td><a href='{{ url_for("reporter.view_aip", aip_id = aip.id) }}'><button type="button" class="btn btn-info">View</button></a></td>
</tr>
{% endfor %}
{% endif %}

{% for aip in pager.items %}
<tr>
<td>{{ aip.transfer_name }}</td>
<td>{{ aip.uuid }}</td>
<td>{{ aip.create_date }}</td>
<td>{{ aip.original_file_count }}</td>
<td>{{ aip.preservation_file_count }}</td>
<td><a href='{{ url_for("reporter.view_aip", aip_id = aip.id) }}'><button type="button" class="btn btn-info">View</button></a></td>
</tr>
{% endfor %}
</table>

{% else %}
Expand All @@ -85,6 +83,29 @@

{% endif %}

{% if pager.total %}

<div class="dataTables_wrapper">
<div class="row">
<div class="col-sm-12 col-md-5">
<div class="dataTables_info" id="aiptable_info" role="status" aria-live="polite">Showing {{ first_item }} to {{ last_item }} of {{ pager.total }} entries</div>
</div>
<div class="col-sm-12 col-md-7">
<div class="dataTables_paginate paging_full_numbers" id="aiptable_paginate">
<ul class="pagination">
<li class="paginate_button page-item first {% if pager.page == 1 %}disabled{% endif %}"><a href="{{ url_for('reporter.view_aips') }}?page=1&{{ state_querystring }}" class="page-link">First</a></li>
<li class="paginate_button page-item previous {% if pager.prev_num is none %}disabled{% endif %}"><a href="{{ url_for('reporter.view_aips') }}?page={{ pager.prev_num }}&{{ state_querystring }}" class="page-link">Previous</a></li>
<li class="paginate_button page-item active"><a href="#" class="page-link">{{ pager.page }}</a></li>
<li class="paginate_button page-item next {% if pager.next_num is none %}disabled{% endif %}"><a href="{{ url_for('reporter.view_aips') }}?page={{ pager.next_num }}&{{ state_querystring }}" class="page-link">Next</a></li>
<li class="paginate_button page-item last {% if pager.page == pager.pages %}disabled{% endif %}"><a href="{{ url_for('reporter.view_aips') }}?page={{ pager.pages }}&{{ state_querystring }}" class="page-link">Last</a></li>
</ul>
</div>
</div>
</div>
</div>

{% endif %}

<script>
$(document).ready(function(){
var storageServiceId = $('#ss').val();
Expand Down
35 changes: 35 additions & 0 deletions AIPscan/Reporter/tests/test_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,3 +129,38 @@ def test_get_premis_xml_lines():
lines = helpers.get_premis_xml_lines(file_)

assert len(lines) == 2


def test_calculate_paging_window():
class MockPagination(object):
pass

pagination = MockPagination()
pagination.per_page = 5
pagination.total = 17

# Test paging window at start of results
first_item, last_item = helpers.calculate_paging_window(1, pagination)

assert first_item == 1
assert last_item == 5

# Test paging window on an arbitrary page of results
first_item, last_item = helpers.calculate_paging_window(3, pagination)

assert first_item == 11
assert last_item == 15

# Test paging window with last item being set to the total of items
first_item, last_item = helpers.calculate_paging_window(4, pagination)

assert first_item == 16
assert last_item == 17


def test_urlencode_without_none_values():
test_values = {"foo": "bar", "nada": None}

querystring = helpers.urlencode_without_none_values(test_values)

assert querystring == "foo=bar&nada="
31 changes: 26 additions & 5 deletions AIPscan/Reporter/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,11 @@
request_params,
sort_puids,
)
from AIPscan.Reporter.helpers import get_premis_xml_lines
from AIPscan.Reporter.helpers import (
calculate_paging_window,
get_premis_xml_lines,
urlencode_without_none_values,
)


def _get_storage_service(storage_service_id):
Expand Down Expand Up @@ -85,10 +89,24 @@ def view_aips():
except Exception as e:
print(e)

aips = AIP.query.filter_by(storage_service_id=storage_service.id)
page = int(request.args.get(request_params.PAGE, default="1"))
per_page = 10

Check warning on line 93 in AIPscan/Reporter/views.py

View check run for this annotation

Codecov / codecov/patch

AIPscan/Reporter/views.py#L92-L93

Added lines #L92 - L93 were not covered by tests

pager = AIP.query.filter_by(storage_service_id=storage_service.id).paginate(

Check warning on line 95 in AIPscan/Reporter/views.py

View check run for this annotation

Codecov / codecov/patch

AIPscan/Reporter/views.py#L95

Added line #L95 was not covered by tests
page=page, per_page=per_page, error_out=False
)

if storage_location:
aips = aips.filter_by(storage_location_id=storage_location_id)
aips = aips.all()
pager = pager.query.filter_by(storage_location_id=storage_location_id).paginate(

Check warning on line 100 in AIPscan/Reporter/views.py

View check run for this annotation

Codecov / codecov/patch

AIPscan/Reporter/views.py#L100

Added line #L100 was not covered by tests
page=page, per_page=per_page, error_out=False
)

first_item, last_item = calculate_paging_window(page, pager)

Check warning on line 104 in AIPscan/Reporter/views.py

View check run for this annotation

Codecov / codecov/patch

AIPscan/Reporter/views.py#L104

Added line #L104 was not covered by tests

state_query_params = {

Check warning on line 106 in AIPscan/Reporter/views.py

View check run for this annotation

Codecov / codecov/patch

AIPscan/Reporter/views.py#L106

Added line #L106 was not covered by tests
request_params.STORAGE_SERVICE_ID: storage_service_id,
request_params.STORAGE_LOCATION_ID: storage_location_id,
}

return render_template(
"aips.html",
Expand All @@ -98,7 +116,10 @@ def view_aips():
storage_service.storage_locations
),
storage_location=storage_location,
aips=aips,
pager=pager,
first_item=first_item,
last_item=last_item,
state_querystring=urlencode_without_none_values(state_query_params),
)


Expand Down

0 comments on commit fc3fe8d

Please sign in to comment.