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

requests: place a request for a patron by a librarian #835

Merged
merged 2 commits into from
Mar 9, 2020
Merged
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 rero_ils/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -1208,6 +1208,10 @@ def _(x):
fields=['name'], title='Location name',
default_order='asc'
)
RECORDS_REST_SORT_OPTIONS['locations']['pickup_name'] = dict(
fields=['pickup_name'], title='Pickup Location name',
default_order='asc'
)
RECORDS_REST_DEFAULT_SORT['locations'] = dict(
query='bestmatch', noquery='name')

Expand Down
58 changes: 57 additions & 1 deletion rero_ils/modules/items/api_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,11 @@
from invenio_circulation.errors import CirculationException
from werkzeug.exceptions import NotFound

from .api import Item
from .api import Item, ItemStatus
from ..circ_policies.api import CircPolicy
from ..libraries.api import Library
from ..loans.api import Loan
from ..loans.utils import can_be_requested
from ..patrons.api import Patron
from ...permissions import librarian_permission

Expand Down Expand Up @@ -323,3 +324,58 @@ def item_availability(item_pid):
return jsonify({
'availability': item.available
})


@api_blueprint.route(
'/<item_pid>/can_request/<library_pid>/<patron_barcode>', methods=['GET'])
@check_authentication
@jsonify_error
def can_request(item_pid, library_pid, patron_barcode):
"""HTTP request to check if a librarian can request an item for a patron.

required_parameters: item_pid, library_pid, patron_barcode
"""
return is_librarian_can_request_item_for_patron(
item_pid, library_pid, patron_barcode)
Comment on lines +333 to +339
Copy link
Contributor

Choose a reason for hiding this comment

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

Why don't using immediately is_librarian_can_request_item_for_patron or insert the code of this method into ?



def jsonify_response(response=False, reason=None):
"""Jsonify api response."""
return jsonify({
'can_request': response,
'reason': reason
})


def is_librarian_can_request_item_for_patron(
item_pid, library_pid, patron_barcode):
"""Check if a librarian can request an item for a patron.

required_parameters: item_pid, library_pid, patron_barcode
"""
item = Item.get_record_by_pid(item_pid)
if not item:
return jsonify_response(reason='Item not found.')
patron = Patron.get_patron_by_barcode(patron_barcode)
if not patron:
return jsonify_response(reason='Patron not found.')
library = Library.get_record_by_pid(library_pid)
if not library:
return jsonify_response(reason='Library not found.')
# Create a loan
loan = Loan({
'patron_pid': patron.pid, 'item_pid': item.pid,
'library_pid': library_pid})
if not can_be_requested(loan):
return jsonify_response(
reason='Circulation policies do not allow request on this item.')
if item.status != ItemStatus.MISSING:
loaned_to_patron = item.is_loaned_to_patron(patron_barcode)
if loaned_to_patron:
return jsonify_response(
reason='Item is already checked-out or requested by patron.')
return jsonify_response(
response=True, reason='Request is possible.')
else:
return jsonify_response(
reason='Item status does not allow requests.')
106 changes: 104 additions & 2 deletions tests/api/test_availability.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,101 @@
from utils import get_json, postdata

from rero_ils.modules.holdings.api import Holding
from rero_ils.modules.items.api import Item
from rero_ils.modules.items.api import Item, ItemStatus
from rero_ils.modules.items.views import item_availability_text
from rero_ils.modules.loans.api import LoanAction


def test_item_can_request(
client, document, holding_lib_martigny, item_lib_martigny,
librarian_martigny_no_email, lib_martigny,
patron_martigny_no_email, circulation_policies,
patron_type_children_martigny):
"""Test item can request API."""
# test no logged user
res = client.get(
url_for(
'api_item.can_request',
item_pid=item_lib_martigny.pid,
library_pid=lib_martigny.pid,
patron_barcode=patron_martigny_no_email.get('barcode')
)
)
assert res.status_code == 401

login_user_via_session(client, librarian_martigny_no_email.user)
# valid test
res = client.get(
url_for(
'api_item.can_request',
item_pid=item_lib_martigny.pid,
library_pid=lib_martigny.pid,
patron_barcode=patron_martigny_no_email.get('barcode')
)
)
assert res.status_code == 200
data = get_json(res)
assert data.get('can_request')

# test no valid item
res = client.get(
url_for(
'api_item.can_request',
item_pid='no_item',
library_pid=lib_martigny.pid,
patron_barcode=patron_martigny_no_email.get('barcode')
)
)
assert res.status_code == 200
data = get_json(res)
assert not data.get('can_request')

# test no valid library
res = client.get(
url_for(
'api_item.can_request',
item_pid=item_lib_martigny.pid,
library_pid='no_library',
patron_barcode=patron_martigny_no_email.get('barcode')
)
)
assert res.status_code == 200
data = get_json(res)
assert not data.get('can_request')

# test no valid patron
res = client.get(
url_for(
'api_item.can_request',
item_pid=item_lib_martigny.pid,
library_pid=lib_martigny.pid,
patron_barcode='no_barcode'
)
)
assert res.status_code == 200
data = get_json(res)
assert not data.get('can_request')

# test no valid item status
item_lib_martigny['status'] = ItemStatus.MISSING
item_lib_martigny.update(item_lib_martigny, dbcommit=True, reindex=True)
res = client.get(
url_for(
'api_item.can_request',
item_pid=item_lib_martigny.pid,
library_pid=lib_martigny.pid,
patron_barcode=patron_martigny_no_email.get('barcode')
)
)
assert res.status_code == 200
data = get_json(res)
assert not data.get('can_request')
item_lib_martigny['status'] = ItemStatus.ON_SHELF
item_lib_martigny.update(item_lib_martigny, dbcommit=True, reindex=True)


def test_item_holding_document_availability(
client, document,
client, document, lib_martigny,
holding_lib_martigny,
item_lib_martigny, item2_lib_martigny,
librarian_martigny_no_email, librarian_saxon_no_email,
Expand Down Expand Up @@ -82,6 +170,7 @@ def test_item_holding_document_availability(
assert document.is_available('global')
assert document_availablity_status(
client, document.pid, librarian_martigny_no_email.user)

# validate request
res, _ = postdata(
client,
Expand Down Expand Up @@ -155,6 +244,19 @@ def test_item_holding_document_availability(
assert document_availablity_status(
client, document.pid, librarian_martigny_no_email.user)

# test can not request item already checked out to patron
res = client.get(
url_for(
'api_item.can_request',
item_pid=item_lib_martigny.pid,
library_pid=lib_martigny.pid,
patron_barcode=patron_martigny_no_email.get('barcode')
)
)
assert res.status_code == 200
data = get_json(res)
assert not data.get('can_request')

class current_i18n:
class locale:
language = 'en'
Expand Down
30 changes: 28 additions & 2 deletions tests/api/test_items_rest.py
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,7 @@ def test_checkout_organisation_policy(client, lib_martigny,

def test_items_requests(client, librarian_martigny_no_email,
patron_martigny_no_email, loc_public_martigny,
item_type_standard_martigny,
item_type_standard_martigny, lib_martigny,
item_lib_martigny, json_header,
circulation_policies):
"""Test requesting an item and validation."""
Expand Down Expand Up @@ -437,6 +437,19 @@ def test_items_requests(client, librarian_martigny_no_email,
assert item.patron_request_rank(patron.get('barcode')) == 1
assert item.is_requested_by_patron(patron.get('barcode'))

# test can not request item already requested to patron
res = client.get(
url_for(
'api_item.can_request',
item_pid=item_pid,
library_pid=lib_martigny.pid,
patron_barcode=patron.get('barcode')
)
)
assert res.status_code == 200
data = get_json(res)
assert not data.get('can_request')

# checkout
res, data = postdata(
client,
Expand Down Expand Up @@ -961,7 +974,7 @@ def test_items_no_extend(client, librarian_martigny_no_email,

def test_items_deny_requests(client, librarian_martigny_no_email,
patron_martigny_no_email, loc_public_martigny,
item_type_standard_martigny,
item_type_standard_martigny, lib_martigny,
item_lib_martigny, json_header,
circ_policy_short_martigny):
"""Test items when requests are denied."""
Expand Down Expand Up @@ -990,6 +1003,19 @@ def test_items_deny_requests(client, librarian_martigny_no_email,
)
assert res.status_code == 403

# test can request because of a circulation policy does not allow request
res = client.get(
url_for(
'api_item.can_request',
item_pid=item_pid,
library_pid=lib_martigny.pid,
patron_barcode=patron.get('barcode')
)
)
assert res.status_code == 200
data = get_json(res)
assert not data.get('can_request')

circ_policy_short_martigny['allow_requests'] = True
circ_policy_short_martigny.update(
data=circ_policy_short_martigny,
Expand Down
18 changes: 0 additions & 18 deletions tests/fixtures/circulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -406,24 +406,6 @@ def system_librarian_sion_no_email(
return ptrn


# ------------ Org: Sion, Lib: Sion, Librarian ----------
@pytest.fixture(scope="module")
@mock.patch('rero_ils.modules.patrons.api.send_reset_password_instructions')
def patron_martigny_no_email(
app,
roles,
patron_type_children_martigny,
patron_martigny_data):
"""Create Martigny patron without sending reset password instruction."""
ptrn = Patron.create(
data=patron_martigny_data,
delete_pid=False,
dbcommit=True,
reindex=True)
flush_index(PatronsSearch.Meta.index)
return ptrn


# ------------ Org: Sion, Lib: Sion, Librarian ----------
@pytest.fixture(scope="module")
def librarian_sion_data(data):
Expand Down