def test_loan_can_be_created_on_missing_item_by_force(client, json_headers, users, testdata): """Test that a patron can request a loan on a missing item by force checkout.""" for user in [users['admin'], users['librarian']]: login_user_via_session(client, email=User.query.get(user.id).email) url = url_for('invenio_app_ils_circulation.loan_create') item_exists = True if search_by_pid( item_pid=NEW_FORCED_LOAN["item_pid"]) \ .execute().hits.total > 0 else False if item_exists and patron_has_active_loan_on_item( patron_pid=NEW_FORCED_LOAN["patron_pid"], item_pid=NEW_FORCED_LOAN["item_pid"]): res = client.post(url, headers=json_headers, data=json.dumps(NEW_FORCED_LOAN)) assert res.status_code == 400 current_item_status = Item.get_record_by_pid( NEW_FORCED_LOAN["item_pid"])["status"] assert current_item_status == Item.get_record_by_pid( NEW_FORCED_LOAN["item_pid"])["status"] elif item_exists: res = client.post(url, headers=json_headers, data=json.dumps(NEW_FORCED_LOAN)) assert res.status_code == 202 loan = json.loads(res.data.decode('utf-8'))['metadata'] assert loan['state'] == 'ITEM_ON_LOAN'
def index_after_loan_replace_item(_, old_item_pid, new_item_pid): """Register Circulation signal to index item.""" if old_item_pid: item = Item.get_record_by_pid(old_item_pid) current_app_ils_extension.item_indexer.index(item) if new_item_pid: item = Item.get_record_by_pid(new_item_pid) current_app_ils_extension.item_indexer.index(item)
def test_checkout_conditions_librarian(client, json_headers, users, testdata): """Test checkout conditions user.""" librarian = users["librarian"] user_login(client, "librarian", users) url = url_for("invenio_app_ils_circulation.loan_checkout") params = deepcopy(NEW_LOAN) params["item_pid"] = dict(type="pitmid", value="itemid-61") params["transaction_user_pid"] = str(librarian.id) res = client.post(url, headers=json_headers, data=json.dumps(params)) assert res.status_code == 202 params = deepcopy(NEW_LOAN) params["item_pid"] = dict(type="pitmid", value="itemid-MISSING") params["transaction_user_pid"] = str(librarian.id) res = client.post(url, headers=json_headers, data=json.dumps(params)) # missing assert res.status_code == 400 params = deepcopy(NEW_LOAN) params["force"] = True params["item_pid"] = dict(type="pitmid", value="itemid-MISSING") params["transaction_user_pid"] = str(librarian.id) res = client.post(url, headers=json_headers, data=json.dumps(params)) # missing but force assert res.status_code == 202 item = Item.get_record_by_pid(params["item_pid"]["value"]) assert item["status"] == "CAN_CIRCULATE"
def create_loan(params, should_force_checkout): """Create a loan on behalf of a user.""" if "patron_pid" not in params: raise MissingRequiredParameterError( description="'patron_pid' is required when creating a loan") if "item_pid" not in params: raise MissingRequiredParameterError( description="'item_pid' is required when creating a loan") if patron_has_active_loan_on_item(patron_pid=params["patron_pid"], item_pid=params["item_pid"]): raise PatronHasLoanOnItemError(params["patron_pid"], params["item_pid"]) if "document_pid" not in params: document_pid = Item.get_document_pid(params["item_pid"]) if document_pid: params["document_pid"] = document_pid if should_force_checkout: _ensure_item_can_circulate(params['item_pid']) # create a new loan record_uuid = uuid.uuid4() new_loan = {"item_pid": params["item_pid"]} pid = loan_pid_minter(record_uuid, data=new_loan) loan = Loan.create(data=new_loan, id_=record_uuid) # trigger the first transition loan = current_circulation.circulation.trigger( loan, **dict(params, trigger="checkout")) return pid, loan
def item_resolver_endpoint(item_pid): """Circulation item resolving view function.""" item = Item.get_record_by_pid(item_pid) # To avoid circular dependencies, we remove loan from item. del item["circulation_status"] del item["document"] return item
def test_update_item_document(client, users, json_headers, testdata, db): """Test REPLACE document pid on item.""" login_user_via_session(client, user=User.query.get(users["admin"].id)) item = Item.get_record_by_pid("itemid-1") item["document_pid"] = "not_found_doc" with pytest.raises(ItemDocumentNotFoundError): item.commit()
def _set_item_to_can_circulate(item_pid): """Change the item status to CAN_CIRCULATE.""" item = Item.get_record_by_pid(item_pid["value"]) if item["status"] != "CAN_CIRCULATE": item["status"] = "CAN_CIRCULATE" item.commit() db.session.commit() current_app_ils.item_indexer.index(item)
def testdata(app, db, es_clear): """Create, index and return test data.""" indexer = RecordIndexer() locations = load_json_from_datadir("locations.json") for location in locations: record = Location.create(location) mint_record_pid(LOCATION_PID_TYPE, Location.pid_field, record) record.commit() db.session.commit() indexer.index(record) internal_locations = load_json_from_datadir("internal_locations.json") iloc_records = [] for internal_location in internal_locations: record = InternalLocation.create(internal_location) mint_record_pid(INTERNAL_LOCATION_PID_TYPE, InternalLocation.pid_field, record) record.commit() iloc_records.append(record) db.session.commit() indexer.index(record) documents = load_json_from_datadir("documents.json") for doc in documents: record = Document.create(doc) mint_record_pid(DOCUMENT_PID_TYPE, Document.pid_field, record) record.commit() db.session.commit() indexer.index(record) items = load_json_from_datadir("items.json") for item in items: record = Item.create(item) mint_record_pid(ITEM_PID_TYPE, Item.pid_field, record) record.commit() db.session.commit() indexer.index(record) loans = load_json_from_datadir("loans.json") for loan in loans: record = Loan.create(loan) mint_record_pid(CIRCULATION_LOAN_PID_TYPE, Loan.pid_field, record) record.commit() db.session.commit() indexer.index(record) # re-index item attached to the loan index_record_after_loan_change(app, record) # flush all indices after indexing, otherwise ES won't be ready for tests current_search.flush_and_refresh(index=None) return { "locations": locations, "documents": documents, "items": items, "loans": loans, }
def index_item_after_document_indexed(document_pid): """Index item to refresh document reference.""" item_pids = circulation_items_retriever(document_pid) item_ids = [] for pid in item_pids: record = Item.get_record_by_pid(pid) item_ids.append(record.id) RecordIndexer().bulk_index(item_ids)
def index_document_after_item_indexed(item_pid): """Index document to re-compute circulation information.""" log_func = partial(_log, origin_rec_type='Item', origin_recid=item_pid, dest_rec_type='Document') log_func(msg=MSG_ORIGIN) document_pid = Item.get_document_pid(item_pid) _index_record_by_pid(Document, document_pid, log_func)
def _ensure_item_can_circulate(item_pid): """Change the item status to CAN_CIRCULATE if different.""" item = Item.get_record_by_pid(item_pid) if item["status"] != "CAN_CIRCULATE": item = item.patch([{ 'op': 'replace', 'path': '/status', 'value': 'CAN_CIRCULATE' }]) item.commit() db.session.commit() current_app_ils_extension.item_indexer.index(item)
def _set_item_to_can_circulate(item_pid): """Change the item status to CAN_CIRCULATE.""" item = Item.get_record_by_pid(item_pid) if item["status"] != "CAN_CIRCULATE": item = item.patch([{ "op": "replace", "path": "/status", "value": "CAN_CIRCULATE" }]) item.commit() db.session.commit() current_app_ils_extension.item_indexer.index(item)
def testdata(app, db, es_clear, system_user): """Create, index and return test data.""" indexer = RecordIndexer() locations = load_json_from_datadir("locations.json") for location in locations: record = Location.create(location) mint_record_pid(LOCATION_PID_TYPE, "pid", record) record.commit() db.session.commit() indexer.index(record) internal_locations = load_json_from_datadir("internal_locations.json") for internal_location in internal_locations: record = InternalLocation.create(internal_location) mint_record_pid(INTERNAL_LOCATION_PID_TYPE, "pid", record) record.commit() db.session.commit() indexer.index(record) documents = load_json_from_datadir("documents.json") for doc in documents: record = Document.create(doc) mint_record_pid(DOCUMENT_PID_TYPE, "pid", record) record.commit() db.session.commit() indexer.index(record) items = load_json_from_datadir("items.json") for item in items: record = Item.create(item) mint_record_pid(ITEM_PID_TYPE, "pid", record) record.commit() db.session.commit() indexer.index(record) loans = load_json_from_datadir("loans.json") for loan in loans: record = Loan.create(loan) mint_record_pid(CIRCULATION_LOAN_PID_TYPE, "pid", record) record.commit() db.session.commit() indexer.index(record) # flush all indices after indexing, otherwise ES won't be ready for tests current_search.flush_and_refresh(index='*') return { "locations": locations, "documents": documents, "items": items, "loans": loans, }
def item_resolver(loan_pid): """Resolve an Item given a Loan PID.""" try: item_pid = get_field_value(Loan, loan_pid, "item_pid") except KeyError: return {} try: item = Item.get_record_by_pid(item_pid) # remove `Document` and `circulation` fields # to avoid circular deps. del item["$schema"] del item["circulation"] del item["document"] except PIDDeletedError: item = {} return item
def test_update_item_status(client, users, json_headers, testdata, db): """Test update item status.""" def get_active_loan_pid_and_item_pid(): LoanSearch = current_circulation.loan_search_cls for t in testdata["items"]: if t["status"] == "CAN_CIRCULATE": item_pid = dict(type=ITEM_PID_TYPE, value=t["pid"]) active_loan = (LoanSearch().get_active_loan_by_item_pid( item_pid).execute().hits) total = (active_loan.total if lt_es7 else active_loan.total.value) if total > 0: return t["pid"], active_loan[0]["pid"] login_user_via_session(client, user=User.query.get(users["admin"].id)) item_pid, loan_pid = get_active_loan_pid_and_item_pid() item = Item.get_record_by_pid(item_pid) with pytest.raises(ItemHasActiveLoanError): item.commit()
def testdata(app, db, es_clear): """Create, index and return test data.""" indexer = RecordIndexer() locations = load_json_from_datadir("locations.json") for location in locations: record = Location.create(location) mint_record_pid(LOCATION_PID_TYPE, "pid", record) record.commit() db.session.commit() indexer.index(record) internal_locations = load_json_from_datadir("internal_locations.json") for internal_location in internal_locations: record = InternalLocation.create(internal_location) mint_record_pid(INTERNAL_LOCATION_PID_TYPE, "pid", record) record.commit() db.session.commit() indexer.index(record) keywords = load_json_from_datadir("keywords.json") for keyword in keywords: record = Keyword.create(keyword) mint_record_pid(KEYWORD_PID_TYPE, "pid", record) record.commit() db.session.commit() indexer.index(record) series_data = load_json_from_datadir("series.json") for series in series_data: record = Series.create(series) mint_record_pid(SERIES_PID_TYPE, "pid", record) record.commit() db.session.commit() indexer.index(record) documents = load_json_from_datadir("documents.json") for doc in documents: record = Document.create(doc) mint_record_pid(DOCUMENT_PID_TYPE, "pid", record) record.commit() db.session.commit() indexer.index(record) items = load_json_from_datadir("items.json") for item in items: record = Item.create(item) mint_record_pid(ITEM_PID_TYPE, "pid", record) record.commit() db.session.commit() indexer.index(record) eitems = load_json_from_datadir("eitems.json") for eitem in eitems: record = EItem.create(eitem) mint_record_pid(EITEM_PID_TYPE, "pid", record) record.commit() db.session.commit() indexer.index(record) loans = load_json_from_datadir("loans.json") for loan in loans: record = Loan.create(loan) mint_record_pid(CIRCULATION_LOAN_PID_TYPE, "pid", record) record.commit() db.session.commit() indexer.index(record) # flush all indices after indexing, otherwise ES won't be ready for tests current_search.flush_and_refresh(index='*') return { "locations": locations, "internal_locations": internal_locations, "documents": documents, "items": items, "loans": loans, "keywords": keywords, "series": series_data, }
def index_item_after_loan_indexed(item_pid): """Index item to re-compute circulation reference.""" if item_pid: item = Item.get_record_by_pid(item_pid) RecordIndexer().index(item)