Skip to content

Commit

Permalink
circulation: block operations for expired patron
Browse files Browse the repository at this point in the history
* Blocks checkout, request and renew circulation operations if the
  patron expiration date is reached.
* Introduces new utils fixtures and adapts unit tests.
* Adapts unit tests to resolve warning pytest deprected messages.
* Closes rero#1495.

Co-Authored-by: Renaud Michotte <renaud.michotte@gmail.com>
  • Loading branch information
zannkukai committed Mar 10, 2021
1 parent 3e29945 commit 903dac1
Show file tree
Hide file tree
Showing 15 changed files with 129 additions and 44 deletions.
4 changes: 4 additions & 0 deletions pytest.ini
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ live_server_scope = module
addopts = --pydocstyle --doctest-glob="*.rst" --doctest-modules --cov=rero_ils --cov-report=term-missing --ignore=setup.py --ignore=docs/conf.py -m "not external"
testpaths = docs tests rero_ils

# custom markers
markers =
external: mark a test as dealing with external services.

# not displaying all the PendingDeprecationWarnings from invenio
filterwarnings =
ignore::PendingDeprecationWarning
33 changes: 29 additions & 4 deletions rero_ils/modules/patrons/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -380,11 +380,16 @@ def can_request(cls, item, **kwargs):
# 'patron' argument are present into kwargs. This check can't
# be relevant --> return True by default
return True, []
# a blocked patron can't request any item

messages = []
# 1) a blocked patron can't request any item
# 2) a patron with obsolete expiration_date can't request any item
if patron.is_blocked:
return False, [patron.get_blocked_message()]
messages.append(patron.get_blocked_message())
if patron.is_expired:
messages.append(_('Patron rights expired.'))

return True, []
return len(messages) == 0, messages

@classmethod
def can_checkout(cls, item, **kwargs):
Expand Down Expand Up @@ -434,9 +439,22 @@ def remove_role(self, role_name):

@property
def patron(self):
"""Patron property shorcut."""
"""Patron property shortcut."""
return self.get('patron', {})

@property
def expiration_date(self):
"""Shortcut to find the patron expiration date."""
date_string = self.patron.get('expiration_date')
if date_string:
return datetime.strptime(date_string, '%Y-%m-%d')

@property
def is_expired(self):
"""Check if patron expiration date is obsolete."""
expiration_date = self.expiration_date
return expiration_date and datetime.now() > expiration_date

@property
def formatted_name(self):
"""Return the best possible human readable patron name."""
Expand Down Expand Up @@ -677,6 +695,13 @@ def get_circulation_messages(self, public=False):
}]

messages = []
# if patron expiration_date has reached - error type message
if self.is_expired:
messages.append({
'type': 'error',
'content': _('Patron rights expired.')
})

# other messages must be only rendered for the professional interface
if not public:
# check the patron type define limit
Expand Down
38 changes: 18 additions & 20 deletions tests/api/circulation/test_actions_views_extend_loan_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
from utils import postdata

from rero_ils.modules.items.models import ItemStatus
from rero_ils.modules.patrons.api import Patron


def test_extend_loan_missing_parameters(
Expand Down Expand Up @@ -77,7 +76,8 @@ def test_extend_loan(
lib_martigny,
loc_public_martigny,
circulation_policies,
item_on_loan_martigny_patron_and_loan_on_loan):
item_on_loan_martigny_patron_and_loan_on_loan,
yesterday):
"""Test frontend extend action."""
login_user_via_session(client, librarian_martigny.user)
item, patron, loan = item_on_loan_martigny_patron_and_loan_on_loan
Expand All @@ -87,31 +87,29 @@ def test_extend_loan(
patron['patron']['blocked'] = True
patron['patron']['blocked_note'] = 'Dummy reason'
patron.update(patron, dbcommit=True, reindex=True)
patron = Patron.get_record_by_pid(patron.pid)

res, _ = postdata(
client,
'api_item.extend_loan',
dict(
item_pid=item.pid,
transaction_user_pid=librarian_martigny.pid,
transaction_location_pid=loc_public_martigny.pid
)
params = dict(
item_pid=item.pid,
transaction_user_pid=librarian_martigny.pid,
transaction_location_pid=loc_public_martigny.pid
)

res, _ = postdata(client, 'api_item.extend_loan', params)
assert res.status_code == 403

# Test for an expired patron
del patron['patron']['blocked']
del patron['patron']['blocked_note']
original_exp_date = patron['patron']['expiration_date']
patron['patron']['expiration_date'] = yesterday.strftime('%Y-%m-%d')
patron.update(patron, dbcommit=True, reindex=True)

res, _ = postdata(client, 'api_item.extend_loan', params)
assert res.status_code == 403

patron['patron']['expiration_date'] = original_exp_date
patron.update(patron, dbcommit=True, reindex=True)

# With only needed parameters
res, _ = postdata(
client,
'api_item.extend_loan',
dict(
item_pid=item.pid,
transaction_user_pid=librarian_martigny.pid,
transaction_location_pid=loc_public_martigny.pid
)
)
res, _ = postdata(client, 'api_item.extend_loan', params)
assert res.status_code == 200
6 changes: 3 additions & 3 deletions tests/api/documents/test_documents_rest.py
Original file line number Diff line number Diff line change
Expand Up @@ -595,9 +595,9 @@ def test_get_remote_cover(mock_get_cover, app):

mock_get_cover.return_value = mock_response(
content='thumb({'
'"success": true,'
'"image": "https:\/\/i.test.com\/images\/P\/XXXXXXXXXX_.jpg"'
'})'
' "success": true,'
' "image": "https://i.test.com/images/P/XXXXXXXXXX_.jpg"'
'})'
)
cover = get_remote_cover('XXXXXXXXXX')
assert cover == {
Expand Down
30 changes: 28 additions & 2 deletions tests/api/patrons/test_patrons_rest.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@

def test_patrons_shortcuts(
client, librarian_martigny, patron_martigny,
librarian_sion):
librarian_sion, yesterday, tomorrow):
"""Test patron shortcuts."""
new_patron = deepcopy(patron_martigny)
assert new_patron.patron_type_pid
Expand All @@ -45,6 +45,17 @@ def test_patrons_shortcuts(
assert not new_patron.organisation_pid
assert new_patron.formatted_name == "Roduit, Louis"

# check for expiration_date
expiration_date = new_patron['patron']['expiration_date']
expiration_date = datetime.strptime(expiration_date, '%Y-%m-%d')
assert new_patron.expiration_date == expiration_date

new_patron['patron']['expiration_date'] = yesterday.strftime('%Y-%m-%d')
assert new_patron.is_expired

new_patron['patron']['expiration_date'] = tomorrow.strftime('%Y-%m-%d')
assert not new_patron.is_expired


def test_filtered_patrons_get(
client, librarian_martigny, patron_martigny,
Expand Down Expand Up @@ -491,7 +502,7 @@ def test_patrons_dirty_barcode(

def test_patrons_circulation_informations(
client, patron_sion, librarian_martigny,
patron3_martigny_blocked):
patron3_martigny_blocked, yesterday, tomorrow):
"""test patron circulation informations."""
url = url_for(
'api_patrons.patron_circulation_informations',
Expand All @@ -516,6 +527,21 @@ def test_patrons_circulation_informations(
assert 'error' == data['messages'][0]['type']
assert 'This patron is currently blocked' in data['messages'][0]['content']

patron = patron3_martigny_blocked
original_expiration_date = patron['patron']['expiration_date']
patron['patron']['expiration_date'] = yesterday.strftime('%Y-%m-%d')
patron['patron']['blocked'] = False
patron.update(patron, dbcommit=True, reindex=True)
res = client.get(url)
data = get_json(res)
assert 'error' == data['messages'][0]['type']
assert 'Patron rights expired.' in data['messages'][0]['content']

# reset the patron
patron['patron']['blocked'] = True
patron['patron']['expiration_date'] = original_expiration_date
patron.update(patron, dbcommit=True, reindex=True)

url = url_for(
'api_patrons.patron_circulation_informations',
patron_pid='dummy_pid'
Expand Down
3 changes: 2 additions & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@
'fixtures.metadata',
'fixtures.organisations',
'fixtures.acquisition',
'fixtures.sip2'
'fixtures.sip2',
'fixtures.basics'
)


Expand Down
34 changes: 34 additions & 0 deletions tests/fixtures/basics.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# -*- coding: utf-8 -*-
#
# RERO ILS
# Copyright (C) 2020 RERO
# Copyright (C) 2020 UCLouvain
#
# 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/>.

"""Common pytest fixtures for rero-ils."""
from datetime import datetime, timedelta, timezone

import pytest


@pytest.fixture(scope="module")
def yesterday():
"""Get yesterday timestamp."""
return datetime.now(timezone.utc) - timedelta(days=1)


@pytest.fixture(scope="module")
def tomorrow():
"""Get tomorrow timestamp."""
return datetime.now(timezone.utc) + timedelta(days=1)
2 changes: 1 addition & 1 deletion tests/ui/documents/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import pytest


@pytest.yield_fixture(scope='module')
@pytest.fixture(scope='module')
def document_records(
document,
document_ref,
Expand Down
11 changes: 4 additions & 7 deletions tests/ui/holdings/test_serial_claims.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@


def test_late_expected_and_claimed_issues(
holding_lib_martigny_w_patterns, holding_lib_sion_w_patterns):
holding_lib_martigny_w_patterns, holding_lib_sion_w_patterns,
yesterday, tomorrow):
"""Test automatic change of late expected issues status to late
and automatic change to claimed when issue is due"""
martigny = holding_lib_martigny_w_patterns
Expand Down Expand Up @@ -56,14 +57,10 @@ def count_issues(holding):
assert count_issues(sion) == [1, 0]

# create a second late issue for martigny and no more for sion
yesterday = (datetime.now(timezone.utc) - timedelta(days=1)
).strftime('%Y-%m-%d')
tomorrow = (datetime.now(timezone.utc) + timedelta(days=1)
).strftime('%Y-%m-%d')
sion['patterns']['next_expected_date'] = tomorrow
sion['patterns']['next_expected_date'] = tomorrow.strftime('%Y-%m-%d')
sion.update(sion, dbcommit=True, reindex=True)

martigny['patterns']['next_expected_date'] = yesterday
martigny['patterns']['next_expected_date'] = yesterday.strftime('%Y-%m-%d')
martigny.update(martigny, dbcommit=True, reindex=True)

msg = process_late_claimed_issues(dbcommit=True, reindex=True)
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/item_types/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import pytest


@pytest.yield_fixture(scope='module')
@pytest.fixture(scope='module')
def item_types_records(
item_type_standard_martigny,
item_type_on_site_martigny,
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/libraries/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import pytest


@pytest.yield_fixture(scope='module')
@pytest.fixture(scope='module')
def libraries_records(
lib_martigny,
lib_saxon,
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/local_fields/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import pytest


@pytest.yield_fixture(scope='module')
@pytest.fixture(scope='module')
def local_fields_records(
local_field_martigny,
local_field_sion,
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/locations/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import pytest


@pytest.yield_fixture(scope='module')
@pytest.fixture(scope='module')
def locations_records(
loc_public_martigny,
loc_restricted_martigny,
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/patron_types/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import pytest


@pytest.yield_fixture(scope='module')
@pytest.fixture(scope='module')
def patron_types_records(
patron_type_adults_martigny,
patron_type_youngsters_sion,
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/patrons/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import pytest


@pytest.yield_fixture(scope='module')
@pytest.fixture(scope='module')
def patrons_records(
patron_martigny,
patron2_martigny,
Expand Down

0 comments on commit 903dac1

Please sign in to comment.