Skip to content

Commit

Permalink
sip2: implement item information
Browse files Browse the repository at this point in the history
Allows the patron to view details of overdue items, checked out items…
on the selfcheck machine. To do that, we need to return item
information (title, fee amount, due date…)

* Upgrades invenio-SIP2 modules version.
* Implements item information handlers.
* Adapts and write some tests.

Co-Authored-by: Lauren-D <[email protected]>
  • Loading branch information
lauren-d committed Dec 10, 2020
1 parent a449f38 commit 1d3fd9c
Show file tree
Hide file tree
Showing 13 changed files with 551 additions and 175 deletions.
10 changes: 5 additions & 5 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ invenio-logging = { version = ">=1.3.0,<1.4.0", extras = ["sentry-sdk", "sentry"
python-dotenv = "^0.13.0"

## Third party optional modules used by RERO ILS
invenio-sip2 = {tag= "v0.2.0", git = "https://github.com/inveniosoftware-contrib/invenio-sip2.git", optional = true}
invenio-sip2 = {tag= "v0.3.0", git = "https://github.com/inveniosoftware-contrib/invenio-sip2.git", optional = true}
flask-cors = ">3.0.8"
celery = "^5.0.0"
lxml = ">=4.6.2"
Expand Down
11 changes: 11 additions & 0 deletions rero_ils/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -2441,5 +2441,16 @@ def _(x):
enable_patron='rero_ils.modules.selfcheck.api:enable_patron',
account='rero_ils.modules.selfcheck.api:patron_information'
),
item_handlers=dict(
item='rero_ils.modules.selfcheck.api:item_information'
),
)
)

SIP2_MEDIA_TYPES = dict(
article='MAGAZINE',
book='BOOK',
journal='MAGAZINE',
sound='AUDIO',
video='VIDEO',
)
28 changes: 18 additions & 10 deletions rero_ils/modules/items/api/circulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
from invenio_circulation.proxies import current_circulation
from invenio_circulation.search.api import search_by_patron_item_or_document, \
search_by_pid
from invenio_i18n.ext import current_i18n
from invenio_pidstore.errors import PersistentIdentifierError
from invenio_records_rest.utils import obj_or_import_string
from invenio_search import current_search
Expand Down Expand Up @@ -1263,18 +1262,27 @@ def available(self):
"""Get availability for item."""
return self.item_has_active_loan_or_request() == 0

def get_item_end_date(self, format='short', time_format='medium'):
"""Get item due date for a given item."""
def get_item_end_date(self, format='short', time_format='medium',
language=None):
"""Get item due date for a given item.
:param format: The date format, ex: 'full', 'medium', 'short'
or custom
:param time_format: The time format, ex: 'medium', 'short' or custom
:param language: The language to fix the language format
:return: original date, formatted date or None
"""
loan = get_loan_for_item(item_pid_to_object(self.pid))
if loan:
end_date = loan['end_date']
due_date = format_date_filter(
end_date,
date_format=format,
time_format=time_format,
locale=current_i18n.locale.language,
)
return due_date
if format:
return format_date_filter(
end_date,
date_format=format,
time_format=time_format,
locale=language,
)
return end_date
return None

def get_extension_count(self):
Expand Down
5 changes: 4 additions & 1 deletion rero_ils/modules/items/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
render_template, url_for
from flask_babelex import gettext as _
from flask_login import current_user
from invenio_i18n.ext import current_i18n
from invenio_records_ui.signals import record_viewed

from .api import Item
Expand Down Expand Up @@ -132,7 +133,9 @@ def item_availability_text(item):
else:
text = ''
if item.status == ItemStatus.ON_LOAN:
due_date = item.get_item_end_date(format='short', time_format=None)
due_date = item.get_item_end_date(
format='short', time_format=None,
language=current_i18n.locale.language)
text = '{msg} {date}'.format(
msg=_('due until'),
date=due_date)
Expand Down
21 changes: 21 additions & 0 deletions rero_ils/modules/patron_transactions/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,27 @@ def get_transactions_by_patron_pid(cls, patron_pid, status=None):
.sort({'creation_date': {'order': 'asc'}})\
.scan()

@classmethod
def get_last_transaction_by_loan_pid(cls, loan_pid, status=None):
"""Return last fee for loan.
:param loan_pid: the loan PID
:param status: the status of transaction
:return: return last transaction transaction
"""
query = PatronTransactionsSearch() \
.filter('term', loan__pid=loan_pid)
if status:
query = query.filter('term', status=status)
results = query\
.sort({'creation_date': {'order': 'desc'}})\
.source('pid').scan()
try:
pid = next(results).pid
return PatronTransaction.get_record_by_pid(pid)
except StopIteration:
pass

@property
def loan_pid(self):
"""Return the loan pid of the the overdue patron transaction."""
Expand Down
1 change: 1 addition & 0 deletions rero_ils/modules/patrons/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -721,6 +721,7 @@ def get_circulation_messages(self, public=False):
* check if the user is blocked ?
* check if the user reaches the maximum loans limit ?
* check if the user reaches the maximum fee amount limit ?
:param public: is messages are for public interface ?
:return an array of messages. Each message is a dictionary with a level
Expand Down
135 changes: 109 additions & 26 deletions rero_ils/modules/selfcheck/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,22 @@

"""Selfcheck API."""

from flask import current_app
from flask_babelex import gettext as _

from .utils import authorize_selfckeck_user, check_sip2_module, \
format_patron_address
format_patron_address, get_patron_status, map_item_circulation_status, \
map_media_type
from ..documents.api import Document
from ..documents.utils import title_format_text_head
from ..items.api import Item
from ..items.models import ItemNoteTypes
from ..libraries.api import Library
from ..loans.api import Loan, LoanState, get_loans_by_patron_pid, \
is_overdue_loan
from ..loans.api import Loan, LoanState, get_loans_by_item_pid_by_patron_pid, \
get_loans_by_patron_pid, is_overdue_loan
from ..patron_transactions.api import PatronTransaction
from ..patrons.api import Patron
from ...filter import format_date_filter


def selfcheck_login(login, password):
Expand All @@ -40,6 +48,7 @@ def selfcheck_login(login, password):
staffer = Patron.get_patron_by_email(login)
return {
'authenticated': staffer.is_librarian,
'user_id': staffer.get('pid'),
'institution_id': staffer.get_organisation().get('code'),
'library_name': Library.get_record_by_pid(
staffer.library_pid).get('name')
Expand Down Expand Up @@ -92,16 +101,9 @@ def enable_patron(barcode, **kwargs):
"""
# check if invenio_sip2 module is present
if check_sip2_module():
from invenio_sip2.models import SelfcheckPatronStatus

patron = Patron.get_patron_by_barcode(barcode)
patron_status = SelfcheckPatronStatus()
# TODO: add PatronStatusType according account
# e.g.:
# patron_status.add_patron_status_type(
# SelfcheckPatronStatusTypes.CARD_REPORTED_LOST)
return {
"patron_status": patron_status,
"patron_status": get_patron_status(patron),
'language': patron.get('communication_language', 'und'),
'institution_id': patron.get_organisation().get('code'),
'patron_id': patron.patron.get('barcode'),
Expand All @@ -121,15 +123,21 @@ def patron_information(barcode, **kwargs):
from invenio_sip2.models import SelfcheckPatronInformation

patron = Patron.get_patron_by_barcode(barcode)

# TODO: add PatronStatusType according account
# e.g.:
# patron_status.add_patron_status_type(
# SelfcheckPatronStatusTypes.CARD_REPORTED_LOST)
patron_account_information = SelfcheckPatronInformation(
patron_id=barcode,
patron_name=patron.formatted_name,
patron_email=patron.get("email"),
patron_phone=patron.get("phone"),
patron_address=format_patron_address(patron),
email=patron.get("email"),
home_phone=patron.get("phone"),
home_address=format_patron_address(patron),
institution_id=patron.get_organisation().get('code'),
language=patron.get('communication_language', 'und'),
currency_type=patron.get_organisation().get('default_currency')
currency_type=patron.get_organisation().get('default_currency'),
valid_patron=patron.is_patron
)

filter_states = [
Expand All @@ -141,27 +149,28 @@ def patron_information(barcode, **kwargs):
loans = get_loans_by_patron_pid(patron.pid, filter_states)
for loan in loans:
item = Item.get_record_by_pid(loan.item_pid)
if loan["state"] == "ITEM_ON_LOAN":
if loan["state"] == LoanState.ITEM_ON_LOAN:
patron_account_information.get('charged_items').append(
item.get("barcode"))
item.get("pid"))
if is_overdue_loan(loan):
patron_account_information.get('overdue_items')\
.append(item.get("barcode"))
.append(item.get("pid"))
elif loan['state'] in [
"PENDING",
"ITEM_AT_DESK",
"ITEM_IN_TRANSIT_FOR_PICKUP"
LoanState.PENDING,
LoanState.ITEM_AT_DESK,
LoanState.ITEM_IN_TRANSIT_FOR_PICKUP
]:
patron_account_information.get('hold_items').append(
item.get("barcode")
item.get("pid")
)

patron_account_information.fee_amount = PatronTransaction \
fee_amount = PatronTransaction \
.get_transactions_total_amount_for_patron(
patron.pid, status='open',
with_subscription=False)
patron_account_information['fee_amount'] = fee_amount
# check for fine items
if patron_account_information.fee_amount > 0:
if fee_amount > 0:
# Check if fine items exist
transaction_pids = PatronTransaction\
.get_transactions_pids_for_patron(
Expand All @@ -175,7 +184,81 @@ def patron_information(barcode, **kwargs):
loan = Loan.get_record_by_pid(transaction.loan_pid)
item = Item.get_record_by_pid(loan.item_pid)
patron_account_information.get('fine_items', []).append(
item.get("barcode")
item.get("pid")
)

return patron_account_information


def item_information(patron_barcode, item_pid, **kwargs):
"""Get item information handler.
get item information according 'item_identifier' from selfcheck user.
:param barcode: barcode of the patron.
:param item_pid: item identifier.
:return: The SelfcheckItemInformation object.
"""
# check if invenio_sip2 module is present
if check_sip2_module():
from invenio_sip2.models import SelfcheckFeeType, \
SelfcheckItemInformation, SelfcheckSecurityMarkerType
patron = Patron.get_patron_by_barcode(patron_barcode)
item = Item.get_record_by_pid(item_pid)
document = Document.get_record_by_pid(item.document_pid)
location = item.get_location()
language = kwargs.get('language', current_app.config
.get('BABEL_DEFAULT_LANGUAGE'))
with current_app.test_request_context() as ctx:
ctx.babel_locale = language
item_information = SelfcheckItemInformation(
item_id=item.get('barcode'),
title_id=title_format_text_head(document.get('title')),
circulation_status=map_item_circulation_status(item.status),
fee_type=SelfcheckFeeType.OTHER,
security_marker=SelfcheckSecurityMarkerType.OTHER
)
item_information['media_type'] = map_media_type(
document.get('type')
)
item_information['hold_queue_length'] = item.number_of_requests()
item_information['owner'] = location.get_library().get('name')
item_information['permanent_location'] = location.get('name')
item_information['current_location'] = \
item.get_last_location().get('name')
# get loan for item
filter_states = [
LoanState.PENDING,
LoanState.ITEM_ON_LOAN
]
loan = get_loans_by_item_pid_by_patron_pid(item_pid, patron.pid,
filter_states)
if loan:
if loan['state'] == LoanState.ITEM_ON_LOAN:
# format the end date according selfcheck language
item_information['due_date'] = format_date_filter(
loan['end_date'],
date_format='short',
time_format=None,
locale=language,
)

transaction = PatronTransaction.\
get_last_transaction_by_loan_pid(
loan_pid=loan.pid,
status='open')
if transaction:
# TODO: map transaction type
item_information['fee_type'] = SelfcheckFeeType.OVERDUE
item_information['fee_amount'] = \
transaction.total_amount
item_information['currency_type'] = \
transaction.currency
item_information.get('screen_messages').append(
_('overdue'))
elif loan['state'] == LoanState.PENDING:
item_information['fee_type'] = SelfcheckFeeType.OTHER
# public note
public_note = item.get_note(ItemNoteTypes.PUBLIC)
if public_note:
item_information.get('screen_messages').append(public_note)

return item_information
Loading

0 comments on commit 1d3fd9c

Please sign in to comment.