def test_overdue_loans(client, librarian_martigny_no_email, patron_martigny_no_email, loc_public_martigny, item_type_standard_martigny, item_lib_martigny, circ_policy_short_martigny): """Test overdue loans.""" login_user_via_session(client, librarian_martigny_no_email.user) item = item_lib_martigny item_pid = item.pid patron_pid = patron_martigny_no_email.pid # checkout res, data = postdata( client, 'api_item.checkout', dict( item_pid=item_pid, patron_pid=patron_pid, transaction_location_pid=loc_public_martigny.pid, transaction_user_pid=librarian_martigny_no_email.pid, ) ) assert res.status_code == 200, "It probably failed while \ test_due_soon_loans fail" loan_pid = data.get('action_applied')[LoanAction.CHECKOUT].get('pid') loan = Loan.get_record_by_pid(loan_pid) end_date = datetime.now(timezone.utc) - timedelta(days=7) loan['end_date'] = end_date.isoformat() loan.update( loan, dbcommit=True, reindex=True ) overdue_loans = get_overdue_loans() assert overdue_loans[0].get('pid') == loan_pid assert number_of_reminders_sent(loan) == 0 loan.create_notification(notification_type='overdue') flush_index(NotificationsSearch.Meta.index) flush_index(LoansSearch.Meta.index) assert number_of_reminders_sent(loan) == 1 # checkin the item to put it back to it's original state res, _ = postdata( client, 'api_item.checkin', dict( item_pid=item_pid, pid=loan_pid, transaction_location_pid=loc_public_martigny.pid, transaction_user_pid=librarian_martigny_no_email.pid, ) ) assert res.status_code == 200
def test_create_over_and_due_soon_notifications_task( client, librarian_martigny_no_email, patron_martigny_no_email, item_lib_martigny, circ_policy_short_martigny, loc_public_martigny, lib_martigny): """Test overdue and due_soon loans.""" login_user_via_session(client, librarian_martigny_no_email.user) item = item_lib_martigny item_pid = item.pid patron_pid = patron_martigny_no_email.pid # checkout res = client.post( url_for('api_item.checkout'), data=json.dumps(dict(item_pid=item_pid, patron_pid=patron_pid)), content_type='application/json', ) assert res.status_code == 200 data = get_json(res) loan_pid = data.get('action_applied')[LoanAction.CHECKOUT].get('pid') loan = Loan.get_record_by_pid(loan_pid) # test due_soon notification end_date = datetime.now(timezone.utc) + timedelta(days=3) loan['end_date'] = end_date.isoformat() loan.update(loan, dbcommit=True, reindex=True) due_soon_loans = get_due_soon_loans() assert due_soon_loans[0].get('pid') == loan_pid create_over_and_due_soon_notifications() flush_index(NotificationsSearch.Meta.index) flush_index(LoansSearch.Meta.index) assert loan.is_notified(notification_type='due_soon') # test overdue notification end_date = datetime.now(timezone.utc) - timedelta(days=7) loan['end_date'] = end_date.isoformat() loan.update(loan, dbcommit=True, reindex=True) overdue_loans = get_overdue_loans() assert overdue_loans[0].get('pid') == loan_pid create_over_and_due_soon_notifications() flush_index(NotificationsSearch.Meta.index) flush_index(LoansSearch.Meta.index) assert loan.is_notified(notification_type='overdue') assert number_of_reminders_sent(loan) == 1 # checkin the item to put it back to it's original state res = client.post( url_for('api_item.checkin'), data=json.dumps(dict(item_pid=item_pid, pid=loan_pid)), content_type='application/json', ) assert res.status_code == 200
def test_item_information(client, librarian_martigny, selfcheck_patron_martigny, loc_public_martigny, item_lib_martigny, circulation_policies): """Test item information.""" login_user_via_session(client, librarian_martigny.user) # checkout res, data = postdata( client, 'api_item.checkout', dict(item_pid=item_lib_martigny.pid, patron_pid=selfcheck_patron_martigny.pid, transaction_user_pid=librarian_martigny.pid, transaction_location_pid=loc_public_martigny.pid)) assert res.status_code == 200 actions = data.get('action_applied') loan_pid = actions[LoanAction.CHECKOUT].get('pid') loan = Loan.get_record_by_pid(loan_pid) assert not loan.is_loan_overdue() # set loan on overdue end_date = datetime.now(timezone.utc) - timedelta(days=7) loan['end_date'] = end_date.isoformat() loan.update(loan, dbcommit=True, reindex=True) loan = Loan.get_record_by_pid(loan_pid) assert loan['state'] == LoanState.ITEM_ON_LOAN assert loan.is_loan_overdue() loan.create_notification( notification_type=Notification.OVERDUE_NOTIFICATION_TYPE) flush_index(NotificationsSearch.Meta.index) flush_index(LoansSearch.Meta.index) assert number_of_reminders_sent(loan) == 1 patron_barcode = selfcheck_patron_martigny\ .get('patron', {}).get('barcode')[0] item_pid = item_lib_martigny.pid # get item information response = item_information(patron_barcode=patron_barcode, item_pid=item_pid) assert response # check required fields in response assert all(key in response for key in ( 'item_id', 'title_id', 'circulation_status', 'fee_type', 'security_marker', )) assert response['due_date'] assert response['fee_amount'] # checkin res, _ = postdata( client, 'api_item.checkin', dict(item_pid=item_lib_martigny.pid, pid=loan_pid, transaction_user_pid=librarian_martigny.pid, transaction_location_pid=loc_public_martigny.pid)) assert res.status_code == 200
def test_patron_information(client, librarian_martigny, selfcheck_patron_martigny, loc_public_martigny, item_lib_martigny, item2_lib_martigny, circulation_policies, lib_martigny): """Test patron information.""" login_user_via_session(client, librarian_martigny.user) # checkout res, data = postdata( client, 'api_item.checkout', dict(item_pid=item_lib_martigny.pid, patron_pid=selfcheck_patron_martigny.pid, transaction_user_pid=librarian_martigny.pid, transaction_location_pid=loc_public_martigny.pid)) assert res.status_code == 200 actions = data.get('action_applied') loan_pid = actions[LoanAction.CHECKOUT].get('pid') loan = Loan.get_record_by_pid(loan_pid) assert not loan.is_loan_overdue() # set loan on overdue end_date = datetime.now(timezone.utc) - timedelta(days=7) loan['end_date'] = end_date.isoformat() loan.update(loan, dbcommit=True, reindex=True) loan = Loan.get_record_by_pid(loan_pid) assert loan.is_loan_overdue() loan.create_notification( notification_type=Notification.OVERDUE_NOTIFICATION_TYPE) flush_index(NotificationsSearch.Meta.index) flush_index(LoansSearch.Meta.index) assert number_of_reminders_sent(loan) == 1 # create request res, data = postdata( client, 'api_item.librarian_request', dict(item_pid=item2_lib_martigny.pid, patron_pid=selfcheck_patron_martigny.pid, pickup_location_pid=loc_public_martigny.pid, transaction_library_pid=lib_martigny.pid, transaction_user_pid=librarian_martigny.pid)) assert res.status_code == 200 # get patron information response = patron_information( selfcheck_patron_martigny.get('patron', {}).get('barcode')[0]) assert response # checkin res, _ = postdata( client, 'api_item.checkin', dict(item_pid=item_lib_martigny.pid, pid=loan_pid, transaction_user_pid=librarian_martigny.pid, transaction_location_pid=loc_public_martigny.pid)) assert res.status_code == 200
def test_overdue_limit(client, app, librarian_martigny, lib_martigny, item_lib_martigny, item2_lib_martigny, patron_type_children_martigny, item3_lib_martigny, item_lib_martigny_data, item2_lib_martigny_data, item3_lib_martigny_data, loc_public_martigny, patron_martigny, circ_policy_short_martigny): """Test overdue limit.""" item = item_lib_martigny item_pid = item.pid patron_pid = patron_martigny.pid # [0] prepare overdue transaction login_user_via_session(client, librarian_martigny.user) # checkout res, data = postdata( client, 'api_item.checkout', dict( item_pid=item_pid, patron_pid=patron_pid, transaction_location_pid=loc_public_martigny.pid, transaction_user_pid=librarian_martigny.pid, )) assert res.status_code == 200 loan_pid = data.get('action_applied')[LoanAction.CHECKOUT].get('pid') loan = Loan.get_record_by_pid(loan_pid) assert not loan.is_loan_overdue() end_date = datetime.now(timezone.utc) - timedelta(days=7) loan['end_date'] = end_date.isoformat() loan.update(loan, dbcommit=True, reindex=True) overdue_loans = list(get_overdue_loans(patron_pid=patron_pid)) assert loan.is_loan_overdue() assert loan.end_date == end_date.isoformat() assert overdue_loans[0].get('pid') == loan_pid assert number_of_reminders_sent(loan) == 0 loan.create_notification( notification_type=Notification.OVERDUE_NOTIFICATION_TYPE) flush_index(NotificationsSearch.Meta.index) flush_index(LoansSearch.Meta.index) assert number_of_reminders_sent(loan) == 1 # [1] test overdue items limit # Update the patron_type to set a overdue_items_limit rule patron_type = patron_type_children_martigny patron_type \ .setdefault('limits', {}) \ .setdefault('overdue_items_limits', {}) \ .setdefault('default_value', 1) patron_type.update(patron_type, dbcommit=True, reindex=True) patron_type = PatronType.get_record_by_pid(patron_type.pid) assert patron_type.get('limits', {}).get('overdue_items_limits', {})\ .get('default_value') == 1 # [1.1] test overdue items limit when we try to checkout a second item res, data = postdata( client, 'api_item.checkout', dict( item_pid=item2_lib_martigny.pid, patron_pid=patron_pid, transaction_location_pid=loc_public_martigny.pid, transaction_user_pid=librarian_martigny.pid, )) assert res.status_code == 403 assert 'Checkout denied' in data['message'] # [1.2] test overdue items limit when we try to request another item res, data = postdata( client, 'api_item.librarian_request', dict(item_pid=item2_lib_martigny.pid, patron_pid=patron_pid, pickup_location_pid=loc_public_martigny.pid, transaction_library_pid=lib_martigny.pid, transaction_user_pid=librarian_martigny.pid)) assert res.status_code == 403 assert 'maximal number of overdue items is reached' in data['message'] # [1.3] test overdue items limit when we try to extend loan 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)) assert res.status_code == 403 assert 'maximal number of overdue items is reached' in data['message'] # reset the patron_type with default value del patron_type['limits'] # [2] test fee amount limit # Update the patron_type to set a fee_amount_limit rule patron_type \ .setdefault('limits', {}) \ .setdefault('fee_amount_limits', {}) \ .setdefault('default_value', 0.5) patron_type.update(patron_type, dbcommit=True, reindex=True) patron_type = PatronType.get_record_by_pid(patron_type.pid) assert patron_type.get('limits', {}).get('fee_amount_limits', {}) \ .get('default_value') == 0.5 # [2.1] test fee amount limit when we try to checkout a second item res, data = postdata( client, 'api_item.checkout', dict( item_pid=item2_lib_martigny.pid, patron_pid=patron_pid, transaction_location_pid=loc_public_martigny.pid, transaction_user_pid=librarian_martigny.pid, )) assert res.status_code == 403 assert 'maximal overdue fee amount is reached' in data['message'] # [2.2] test fee amount limit when we try to request another item res, data = postdata( client, 'api_item.librarian_request', dict(item_pid=item2_lib_martigny.pid, patron_pid=patron_pid, pickup_location_pid=loc_public_martigny.pid, transaction_library_pid=lib_martigny.pid, transaction_user_pid=librarian_martigny.pid)) assert res.status_code == 403 assert 'maximal overdue fee amount is reached' in data['message'] # [2.3] test fee amount limit when we try to extend loan 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)) assert res.status_code == 403 assert 'maximal overdue fee amount is reached' in data['message'] # reset the patron_type with default value del patron_type['limits'] patron_type.update(patron_type, dbcommit=True, reindex=True) patron_type = PatronType.get_record_by_pid(patron_type.pid) assert patron_type.get('limits') is None # # checkin the item to put it back to it's original state res, _ = postdata( client, 'api_item.checkin', dict( item_pid=item_pid, pid=loan_pid, transaction_location_pid=loc_public_martigny.pid, transaction_user_pid=librarian_martigny.pid, )) assert res.status_code == 200
def test_overdue_loans(client, librarian_martigny, patron_martigny, loc_public_martigny, item_type_standard_martigny, item_lib_martigny, item2_lib_martigny, patron_type_children_martigny, circ_policy_short_martigny, patron3_martigny_blocked): """Test overdue loans.""" login_user_via_session(client, librarian_martigny.user) item = item_lib_martigny item_pid = item.pid patron_pid = patron_martigny.pid # checkout res, data = postdata( client, 'api_item.checkout', dict( item_pid=item_pid, patron_pid=patron_pid, transaction_location_pid=loc_public_martigny.pid, transaction_user_pid=librarian_martigny.pid, )) assert res.status_code == 200, "It probably failed while \ test_due_soon_loans fail" loan_pid = data.get('action_applied')[LoanAction.CHECKOUT].get('pid') loan = Loan.get_record_by_pid(loan_pid) end_date = datetime.now(timezone.utc) - timedelta(days=7) loan['end_date'] = end_date.isoformat() loan.update(loan, dbcommit=True, reindex=True) overdue_loans = list(get_overdue_loans(patron_pid=patron_pid)) assert overdue_loans[0].get('pid') == loan_pid assert number_of_reminders_sent(loan) == 0 loan.create_notification( notification_type=Notification.OVERDUE_NOTIFICATION_TYPE) flush_index(NotificationsSearch.Meta.index) flush_index(LoansSearch.Meta.index) assert number_of_reminders_sent(loan) == 1 # Try a checkout for a blocked user :: It should be blocked res, data = postdata( client, 'api_item.checkout', dict( item_pid=item2_lib_martigny.pid, patron_pid=patron3_martigny_blocked.pid, transaction_location_pid=loc_public_martigny.pid, transaction_user_pid=librarian_martigny.pid, )) assert res.status_code == 403 assert 'This patron is currently blocked' in data['message'] # checkin the item to put it back to it's original state res, _ = postdata( client, 'api_item.checkin', dict( item_pid=item_pid, pid=loan_pid, transaction_location_pid=loc_public_martigny.pid, transaction_user_pid=librarian_martigny.pid, )) assert res.status_code == 200
def test_notifications_task(client, librarian_martigny, patron_martigny, item_lib_martigny, circ_policy_short_martigny, loc_public_martigny, lib_martigny): """Test overdue and due_soon loans.""" login_user_via_session(client, librarian_martigny.user) item = item_lib_martigny item_pid = item.pid patron_pid = patron_martigny.pid # First we need to create a checkout res, data = postdata( client, 'api_item.checkout', dict( item_pid=item_pid, patron_pid=patron_pid, transaction_location_pid=loc_public_martigny.pid, transaction_user_pid=librarian_martigny.pid, )) assert res.status_code == 200 loan_pid = data.get('action_applied')[LoanAction.CHECKOUT].get('pid') loan = Loan.get_record_by_pid(loan_pid) # test due_soon notification end_date = datetime.now(timezone.utc) + timedelta(days=3) loan['end_date'] = end_date.isoformat() loan.update(loan, dbcommit=True, reindex=True) due_soon_loans = get_due_soon_loans() assert due_soon_loans[0].get('pid') == loan_pid create_notifications(types=[ Notification.DUE_SOON_NOTIFICATION_TYPE, Notification.OVERDUE_NOTIFICATION_TYPE ]) flush_index(NotificationsSearch.Meta.index) flush_index(LoansSearch.Meta.index) assert loan.is_notified(Notification.DUE_SOON_NOTIFICATION_TYPE) # test overdue notification # For this test, we will update the loan to simulate an overdue of 12 # days. With this delay, regarding the cipo configuration, only the first # overdue reminder should be sent. # NOTE : the cipo define the first overdue reminder after 5 days. But we # use an overdue of 12 days because the overdue is based on # loan->item->library open days. Using 12 (5 days + 1 week) we # ensure than the overdue notification will be sent. end_date = datetime.now(timezone.utc) - timedelta(days=12) loan['end_date'] = end_date.isoformat() loan.update(loan, dbcommit=True, reindex=True) overdue_loans = list(get_overdue_loans()) assert overdue_loans[0].get('pid') == loan_pid create_notifications(types=[ Notification.DUE_SOON_NOTIFICATION_TYPE, Notification.OVERDUE_NOTIFICATION_TYPE ], process=False) flush_index(NotificationsSearch.Meta.index) flush_index(LoansSearch.Meta.index) assert loan.is_notified(Notification.OVERDUE_NOTIFICATION_TYPE, 0) assert number_of_reminders_sent( loan, notification_type=Notification.OVERDUE_NOTIFICATION_TYPE) == 1 # test overdue notification#2 # Now simulate than the previous call crashed. So call the task with a # fixed date. In our test, no new notifications should be sent create_notifications(types=[ Notification.DUE_SOON_NOTIFICATION_TYPE, Notification.OVERDUE_NOTIFICATION_TYPE ], tstamp=datetime.now(timezone.utc), process=False) assert number_of_reminders_sent( loan, notification_type=Notification.OVERDUE_NOTIFICATION_TYPE) == 1 # test overdue notification#3 # For this test, we will update the loan to simulate an overdue of 40 # days. With this delay, regarding the cipo configuration, the second # (and last) overdue reminder should be sent. end_date = datetime.now(timezone.utc) - timedelta(days=40) loan['end_date'] = end_date.isoformat() loan.update(loan, dbcommit=True, reindex=True) overdue_loans = list(get_overdue_loans()) assert overdue_loans[0].get('pid') == loan_pid create_notifications(types=[ Notification.DUE_SOON_NOTIFICATION_TYPE, Notification.OVERDUE_NOTIFICATION_TYPE ], process=False) flush_index(NotificationsSearch.Meta.index) flush_index(LoansSearch.Meta.index) assert loan.is_notified(Notification.OVERDUE_NOTIFICATION_TYPE, 1) assert number_of_reminders_sent( loan, notification_type=Notification.OVERDUE_NOTIFICATION_TYPE) == 2 # checkin the item to put it back to it's original state res, _ = postdata( client, 'api_item.checkin', dict(item_pid=item_pid, pid=loan_pid, transaction_location_pid=loc_public_martigny.pid, transaction_user_pid=librarian_martigny.pid)) assert res.status_code == 200