Skip to content

Commit

Permalink
api: add missing transition conditions
Browse files Browse the repository at this point in the history
* Implement checkin, request, validate_request circulation policies
  using states conditions. (closes inveniosoftware#28)

Signed-off-by: Johnny Mariéthoz <Johnny.Mariethoz@rero.ch>
  • Loading branch information
jma committed Jul 19, 2018
1 parent 23c73db commit c69690e
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 41 deletions.
50 changes: 36 additions & 14 deletions invenio_circulation/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,24 @@
'trigger': 'request',
'source': 'CREATED',
'dest': 'PENDING',
'before': 'set_request_parameters'
'before': 'set_request_parameters',
'conditions': 'is_request_valid'
}, {
'trigger': 'validate_request',
'source': 'PENDING',
'dest': 'ITEM_IN_TRANSIT',
'before': 'set_parameters',
'conditions': 'is_validate_request_valid',
'unless': 'is_pickup_at_same_library'
}, {
'trigger': 'validate_request',
'source': 'PENDING',
'dest': 'ITEM_AT_DESK',
'before': 'set_parameters',
'conditions': 'is_pickup_at_same_library'
'conditions': [
'is_validate_request_valid',
'is_pickup_at_same_library'
]
}, {
'trigger': 'checkout',
'source': 'CREATED',
Expand All @@ -43,7 +48,8 @@
'trigger': 'checkin',
'source': 'ITEM_ON_LOAN',
'dest': 'ITEM_RETURNED',
'before': 'set_parameters'
'before': 'set_parameters',
'conditions': 'is_checkin_valid'
}]


Expand All @@ -59,6 +65,11 @@ def __init__(self, data, model=None):
initial=self['state'],
finalize_event='save')

@property
def policies(self):
"""."""
return current_app.config.get('CIRCULATION_POLICIES')

def set_request_parameters(self, event):
"""."""
self.set_parameters(event)
Expand All @@ -75,17 +86,20 @@ def set_parameters(self, event):
self['transaction_date'] = params.get('transaction_date',
datetime.now().isoformat())

def save(self, event):
"""."""
if event.error:
raise event.error
else:
self['state'] = self.state
self.commit()

def is_pickup_at_same_library(self, event):
"""."""
item_location_pid = current_app.config.get(
'CIRCULATION_ITEM_LOCATION_RETRIEVER')(self['item_pid'])
return self['pickup_location_pid'] == item_location_pid

@property
def policies(self):
"""."""
return current_app.config.get('CIRCULATION_POLICIES')

def is_checkout_valid(self, event):
"""."""
dates = self.policies['checkout'](**event.kwargs)
Expand All @@ -94,10 +108,18 @@ def is_checkout_valid(self, event):
self['start_date'], self['end_date'] = dates
return True

def save(self, event):
def is_checkin_valid(self, event):
"""."""
if event.error:
raise Exception(event.error)
else:
self['state'] = self.state
self.commit()
return self.policies['checkin'](**event.kwargs)

def is_request_valid(self, event):
"""."""
pickup_location_pid = self.policies['request'](**event.kwargs)
if not pickup_location_pid:
return False
self['pickup_location_pid'] = pickup_location_pid
return True

def is_validate_request_valid(self, event):
"""."""
return self.policies['validate_request'](**event.kwargs)
10 changes: 8 additions & 2 deletions invenio_circulation/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
# TODO: This is an example file. Remove it if your package does not use any
# extra configuration variables.

from .utils import is_checkout_valid, item_location_retriever
from .utils import is_checkin_valid, is_checkout_valid, is_request_valid, \
is_request_validate_valid, item_location_retriever

CIRCULATION_DEFAULT_VALUE = 'foobar'
"""Default value for the application."""
Expand All @@ -20,4 +21,9 @@

CIRCULATION_ITEM_LOCATION_RETRIEVER = item_location_retriever

CIRCULATION_POLICIES = dict(checkout=is_checkout_valid)
CIRCULATION_POLICIES = dict(
checkout=is_checkout_valid,
checkin=is_checkin_valid,
request=is_request_valid,
validate_request=is_request_validate_valid
)
28 changes: 28 additions & 0 deletions invenio_circulation/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,31 @@ def is_checkout_valid(transaction_user_pid,
if not end_date:
end_date = start_date + timedelta(days=30)
return (start_date.strftime('%Y-%m-%d'), end_date.strftime('%Y-%m-%d'))


def is_request_valid(transaction_user_pid,
patron_pid,
item_pid,
transaction_location_pid,
transaction_date,
pickup_location_pid):
"""."""
return pickup_location_pid


def is_request_validate_valid(transaction_user_pid,
patron_pid,
item_pid,
transaction_location_pid,
transaction_date):
"""."""
return True


def is_checkin_valid(transaction_user_pid,
patron_pid,
item_pid,
transaction_location_pid,
transaction_date):
"""."""
return True
7 changes: 7 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from invenio_records.ext import InvenioRecords
from sqlalchemy_utils.functions import create_database, database_exists

from invenio_circulation.api import Loan
from invenio_circulation.ext import InvenioCirculation


Expand All @@ -33,6 +34,12 @@ def instance_path():
shutil.rmtree(path)


@pytest.yield_fixture()
def loan(db):
"""Minimal Loan object."""
yield Loan.create({})


@pytest.fixture()
def params():
"""."""
Expand Down
61 changes: 36 additions & 25 deletions tests/test_loan.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

"""Tests for loan states."""

import mock

from invenio_circulation.api import Loan

Expand All @@ -24,18 +23,8 @@ def test_loan_creation(db, params):
assert loan.state == 'ITEM_RETURNED'


def test_conditional_checkout(app, db, params):
"""Test loan checkout and checkin."""
loan = Loan.create({})
loan.checkout(**params)
assert loan.state == 'ITEM_ON_LOAN'
assert loan['start_date'] == params.get('transaction_date')


def test_storing_loan_parameters(db, params):
def test_storing_loan_parameters(loan, db, params):
"""."""
loan = Loan.create({})

loan.checkout(**params)

params['state'] = 'ITEM_ON_LOAN'
Expand All @@ -44,39 +33,61 @@ def test_storing_loan_parameters(db, params):
assert loan == params


def test_persist_loan(db, params):
def test_persist_loan(loan, db, params):
"""."""
loan = Loan.create({})
loan.checkout(**params)
db.session.commit()

loan = Loan.get_record(loan.id)
assert loan.state == 'ITEM_ON_LOAN'


def test_validate_item_in_transit(app, db, params):
def test_validate_item_in_transit(loan, app, db, params):
"""."""
loan = Loan.create({})
params['pickup_location_pid'] = 'pickup_location_pid'

loan.request(**params)
loan.request(**params, pickup_location_pid='pickup_location_pid')
assert loan.state == 'PENDING'

app.config['CIRCULATION_ITEM_LOCATION_RETRIEVER'] =\
lambda x: 'external_location_pid'
loan.validate_request()
loan.validate_request(**params)
assert loan.state == 'ITEM_IN_TRANSIT'


def test_validate_item_at_desk(app, db, params):
def test_validate_item_at_desk(loan, app, db, params):
"""."""
loan = Loan.create({})
params['pickup_location_pid'] = 'pickup_location_pid'
loan.request(**params, pickup_location_pid='pickup_location_pid')
assert loan.state == 'PENDING'

app.config['CIRCULATION_ITEM_LOCATION_RETRIEVER'] =\
lambda x: 'pickup_location_pid'
loan.validate_request(**params)
assert loan.state == 'ITEM_AT_DESK'


def test_conditional_checkout(loan, app, db, params):
"""Test checkout with some conditions."""
loan.checkout(**params)
assert loan.state == 'ITEM_ON_LOAN'
assert loan['start_date'] == params.get('transaction_date')

loan.request(**params)

def test_conditional_checkin(loan, app, db, params):
"""Test checkin with some conditions."""
loan.checkout(**params)
loan.checkin(**params)
assert loan.state == 'ITEM_RETURNED'


def test_conditional_request(loan, app, db, params):
"""Test checkin with some conditions."""
loan.request(**params, pickup_location_pid='pickup_location_pid')
assert loan.state == 'PENDING'


def test_conditional_validate_request(loan, app, db, params):
"""Test checkin with some conditions."""
loan.request(**params, pickup_location_pid='pickup_location_pid')
app.config['CIRCULATION_ITEM_LOCATION_RETRIEVER'] =\
lambda x: 'pickup_location_pid'
loan.validate_request()
loan.validate_request(**params)
assert loan.state == 'ITEM_AT_DESK'

0 comments on commit c69690e

Please sign in to comment.