Skip to content

Commit

Permalink
items: can request api for an item
Browse files Browse the repository at this point in the history
* Removes duplicate fixtures function.

Co-Authored-by: Aly Badr <[email protected]>
  • Loading branch information
Aly Badr committed Mar 4, 2020
1 parent bb70fbb commit 3b6ffc7
Show file tree
Hide file tree
Showing 5 changed files with 217 additions and 22 deletions.
14 changes: 14 additions & 0 deletions rero_ils/modules/items/api_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
from werkzeug.exceptions import NotFound

from .api import Item
from .utils import is_librarian_can_request_item_for_patron
from ..circ_policies.api import CircPolicy
from ..libraries.api import Library
from ..loans.api import Loan
Expand Down Expand Up @@ -323,3 +324,16 @@ 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)
71 changes: 71 additions & 0 deletions rero_ils/modules/items/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# -*- coding: utf-8 -*-
#
# RERO ILS
# Copyright (C) 2019 RERO
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, version 3 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

"""Item utils."""

from flask import jsonify
from flask_babelex import gettext as _

from .api import Item, ItemStatus
from ..libraries.api import Library
from ..loans.api import Loan
from ..loans.utils import can_be_requested
from ..patrons.api import Patron


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

0 comments on commit 3b6ffc7

Please sign in to comment.