def test_patron_transaction_jsonresolver(patron_transaction_overdue_martigny): """Test patron_transaction json resolver.""" rec = Record.create( {'patron_transaction': { '$ref': 'https://ils.rero.ch/api/patron_transactions/1' } } ) assert extracted_data_from_ref(rec.get('patron_transaction')) == '1' # delete attached events to patron transaction for patron_event in patron_transaction_overdue_martigny.events: patron_event.delete(dbcommit=True, delindex=True) # deleted record patron_transaction_overdue_martigny.delete() with pytest.raises(JsonRefError): rec.replace_refs().dumps() # non existing record rec = Record.create( {'patron_transaction': { '$ref': 'https://ils.rero.ch/api/patron_transactions/n_e'}} ) with pytest.raises(JsonRefError): rec.replace_refs().dumps()
def loan_overdue_martigny(app, document, item4_lib_martigny, loc_public_martigny, item_type_standard_martigny, librarian_martigny, patron_martigny, circulation_policies): """Checkout an item to a patron. item4_lib_martigny is overdue. """ transaction_date = datetime.now(timezone.utc).isoformat() item4_lib_martigny.checkout( patron_pid=patron_martigny.pid, transaction_location_pid=loc_public_martigny.pid, transaction_user_pid=librarian_martigny.pid, transaction_date=transaction_date, document_pid=extracted_data_from_ref( item4_lib_martigny.get('document'))) flush_index(ItemsSearch.Meta.index) flush_index(LoansSearch.Meta.index) loan = Loan.get_record_by_pid( item4_lib_martigny.get_loan_pid_with_item_on_loan( item4_lib_martigny.pid)) end_date = datetime.now(timezone.utc) - timedelta(days=25) loan['end_date'] = end_date.isoformat() loan = loan.update(loan, dbcommit=True, reindex=True) return loan
def update(cls, user, record): """Update permission check. :param user: Logged user. :param record: Record to check. :return: True is action can be done. """ # User must be be authenticated and have (at least) librarian role if not current_librarian: return False if current_librarian and not record: # legacy return True # * User can only update record of its own organisation # - 'sys_lib' could always update a record # - 'lib' could only update cipo, if : # --> cipo is defined at the library level # --> current user library is into the cipo libraries list if current_librarian.organisation_pid == record.organisation_pid: if current_librarian.is_system_librarian: return True # librarian elif record.get('policy_library_level', False): cipo_library_pids = [] for library in record.get('libraries', []): cipo_library_pids.append(extracted_data_from_ref(library)) # Intersection patron libraries pid and cipo library pids return len( set(current_librarian.library_pids).intersection( cipo_library_pids)) > 0 return False
def create_collections(input_file, max_item=10): """Create collections.""" organisation_items = {} with open(input_file, 'r', encoding='utf-8') as request_file: collections = json.load(request_file) for collection_data in collections: organisation_pid = extracted_data_from_ref( collection_data.get('organisation').get('$ref')) if organisation_pid not in organisation_items: organisation_items[organisation_pid] =\ get_items_by_organisation_pid(organisation_pid) items = random.choices( organisation_items[organisation_pid], k=random.randint(1, max_item) ) collection_data['items'] = [] for item_pid in items: ref = get_ref_for_pid('items', item_pid) collection_data['items'].append({'$ref': ref}) request = Collection.create( collection_data, dbcommit=True, reindex=True ) click.echo('\tCollection: #{pid}'.format( pid=request.pid, ))
def test_local_fields_es_mapping(db, org_sion, document, local_field_sion_data): """Test local fields elasticsearch mapping.""" search = LocalFieldsSearch() mapping = get_mapping(search.Meta.index) assert mapping lofi = LocalField.create(local_field_sion_data, dbcommit=True, reindex=True, delete_pid=True) flush_index(LocalFieldsSearch.Meta.index) assert mapping == get_mapping(search.Meta.index) assert lofi == local_field_sion_data assert lofi.get('pid') == '1' lofi = LocalField.get_record_by_pid('1') assert lofi == local_field_sion_data fetched_pid = fetcher(lofi.id, lofi) assert fetched_pid.pid_value == '1' assert fetched_pid.pid_type == 'lofi' document_pid = extracted_data_from_ref(lofi.get('parent')) search = DocumentsSearch().filter('term', pid=document_pid) document = list(search.scan())[0].to_dict() for field in document['local_fields']: if field['organisation_pid'] == document_pid: assert field['fields'] ==\ local_field_sion_data['fields']['field_1']
def test_patron_type_exist_name_and_organisation_pid( patron_type_children_martigny): """Test patron type name uniquness.""" org_pid = extracted_data_from_ref( patron_type_children_martigny.get('organisation')) assert PatronType.exist_name_and_organisation_pid( patron_type_children_martigny.get('name'), org_pid) assert not PatronType.exist_name_and_organisation_pid( 'not exists yet', org_pid)
def test_extract_data_from_ref(app, patron_sion_data, patron_type_grown_sion): """Test extract_data_from_ref.""" # Check real data ptty = patron_sion_data['patron']['type'] assert extracted_data_from_ref(ptty, data='pid') == 'ptty4' assert extracted_data_from_ref(ptty, data='resource') == 'patron_types' assert extracted_data_from_ref(ptty, data='record_class') == PatronType ptty_record = extracted_data_from_ref(ptty, data='record') assert ptty_record.pid == patron_type_grown_sion.pid # check dummy data assert extracted_data_from_ref('dummy_data', data='pid') is None assert extracted_data_from_ref('dummy_data', data='resource') is None assert extracted_data_from_ref('dummy_data', data='record_class') is None assert extracted_data_from_ref('dummy_data', data='record') is None assert extracted_data_from_ref(ptty, data='dummy') is None
def loan_validated_martigny(app, document, item2_lib_martigny, loc_public_martigny, item_type_standard_martigny, librarian_martigny, patron_martigny, circulation_policies): """Request and validate item to a patron. item2_lib_martigny is requested and validated to patron_martigny. """ transaction_date = datetime.now(timezone.utc).isoformat() item2_lib_martigny.request( patron_pid=patron_martigny.pid, transaction_location_pid=loc_public_martigny.pid, transaction_user_pid=librarian_martigny.pid, transaction_date=transaction_date, pickup_location_pid=loc_public_martigny.pid, document_pid=extracted_data_from_ref( item2_lib_martigny.get('document'))) flush_index(ItemsSearch.Meta.index) flush_index(LoansSearch.Meta.index) flush_index(NotificationsSearch.Meta.index) loan = list( item2_lib_martigny.get_loans_by_item_pid( item_pid=item2_lib_martigny.pid))[0] item2_lib_martigny.validate_request( pid=loan.pid, patron_pid=patron_martigny.pid, transaction_location_pid=loc_public_martigny.pid, transaction_user_pid=librarian_martigny.pid, transaction_date=transaction_date, pickup_location_pid=loc_public_martigny.pid, document_pid=extracted_data_from_ref( item2_lib_martigny.get('document'))) flush_index(ItemsSearch.Meta.index) flush_index(LoansSearch.Meta.index) flush_index(NotificationsSearch.Meta.index) loan = list( item2_lib_martigny.get_loans_by_item_pid( item_pid=item2_lib_martigny.pid))[0] return loan
def migrate_virtua_operation_logs(infile, verbose, debug, lazy): """Migrate Virtua operation log records in reroils. :param infile: Json operation log file. :param lazy: lazy reads file """ enabled_logs = current_app.config.get('RERO_ILS_ENABLE_OPERATION_LOG') click.secho('Migrate Virtua operation log records:', fg='green') if lazy: # try to lazy read json file (slower, better memory management) data = read_json_record(infile) else: # load everything in memory (faster, bad memory management) data = json.load(infile) index_count = 0 with click.progressbar(data) as bar: for oplg in bar: try: operation = oplg.get('operation') resource = extracted_data_from_ref( oplg.get('record').get('$ref'), data='resource') pid_type = enabled_logs.get(resource) if pid_type and operation == OperationLogOperation.CREATE: # The virtua create operation log overrides the reroils # create operation log, the method to use is UPDATE record_pid = extracted_data_from_ref( oplg.get('record').get('$ref'), data='pid') create_rec = \ OperationLog.get_create_operation_log_by_resource_pid( pid_type, record_pid) if create_rec: create_rec.update(oplg, dbcommit=True, reindex=True) elif pid_type and operation == OperationLogOperation.UPDATE: # The virtua update operation log is a new entry in the # reroils operation log, the method to use is CREATE OperationLog.create(data=oplg, dbcommit=True, reindex=True) except Exception: pass index_count += len(data) click.echo(f'created {index_count} operation logs.')
def test_acq_receipts_jsonresolver(acq_receipt_fiction_martigny): """Acquisition receipts resolver tests.""" data = {'$ref': 'https://bib.rero.ch/api/acq_receipts/acre1'} rec = Record.create({'acq_receipt': data}) assert extracted_data_from_ref(rec.get('acq_receipt')) == 'acre1' # deleted record acq_receipt_fiction_martigny.delete() with pytest.raises(JsonRefError): rec.replace_refs().dumps() # non existing record data = {'$ref': 'https://bib.rero.ch/api/acq_receipts/n_e'} rec = Record.create({'acq_receipt': data}) with pytest.raises(JsonRefError): rec.replace_refs().dumps()
def test_budgets_jsonresolver(budget_2017_martigny): """Budgets resolver tests.""" rec = Record.create({ 'budget': {'$ref': 'https://ils.rero.ch/api/budgets/budg5'} }) assert extracted_data_from_ref(rec.get('budget')) == 'budg5' # deleted record budget_2017_martigny.delete() with pytest.raises(JsonRefError): rec.replace_refs().dumps() # non existing record rec = Record.create({ 'budget': {'$ref': 'https://ils.rero.ch/api/budgets/n_e'} }) with pytest.raises(JsonRefError): rec.replace_refs().dumps()
def test_acq_orders_jsonresolver(acq_order_fiction_martigny): """Acquisition orders resolver tests.""" rec = Record.create( {'acq_order': { '$ref': 'https://ils.rero.ch/api/acq_orders/acor1' }}) assert extracted_data_from_ref(rec.get('acq_order')) == 'acor1' # deleted record acq_order_fiction_martigny.delete() with pytest.raises(JsonRefError): rec.replace_refs().dumps() # non existing record rec = Record.create( {'acq_order': { '$ref': 'https://ils.rero.ch/api/acq_orders/n_e' }}) with pytest.raises(JsonRefError): rec.replace_refs().dumps()
def loan_pending_martigny(app, item_lib_fully, loc_public_martigny, librarian_martigny, patron2_martigny, circulation_policies): """Create loan record with state pending. item_lib_fully is requested by patron2_martigny. """ transaction_date = datetime.now(timezone.utc).isoformat() item_lib_fully.request(patron_pid=patron2_martigny.pid, transaction_location_pid=loc_public_martigny.pid, transaction_user_pid=librarian_martigny.pid, transaction_date=transaction_date, pickup_location_pid=loc_public_martigny.pid, document_pid=extracted_data_from_ref( item_lib_fully.get('document'))) flush_index(ItemsSearch.Meta.index) flush_index(LoansSearch.Meta.index) loan = list( item_lib_fully.get_loans_by_item_pid(item_pid=item_lib_fully.pid))[0] return loan
def test_vendors_jsonresolver(app, vendor_martigny): """Test vendor resolver.""" rec = Record.create({ 'vendor': {'$ref': 'https://bib.rero.ch/api/vendors/vndr1'} }) assert extracted_data_from_ref(rec.get('vendor')) == 'vndr1' # deleted record vendor_martigny.delete() with pytest.raises(Exception): rec.replace_refs().dumps() # non existing record rec = Record.create({ 'vendor': {'$ref': 'https://bib.rero.ch/api/vendors/n_e'} }) with pytest.raises(JsonRefError) as error: rec.replace_refs().dumps() assert 'PIDDoesNotExistError' in str(error)
def test_local_field_jsonresolver(local_field_martigny): """Test local fields json resolver.""" local_field = local_field_martigny rec = Record.create({ 'local_field': { '$ref': 'https://bib.rero.ch/api/local_fields/lofi1' } }) assert extracted_data_from_ref(rec.get('local_field')) == 'lofi1' # deleted record local_field.delete() with pytest.raises(JsonRefError): rec.replace_refs().dumps() # non existing record rec = Record.create( {'local_fields': { '$ref': 'https://bib.rero.ch/api/local_fields/n_e' }}) with pytest.raises(JsonRefError): rec.replace_refs().dumps()
def test_receipts_properties(acq_order_fiction_martigny, acq_account_fiction_martigny, acq_receipt_fiction_martigny, acq_receipt_line_1_fiction_martigny, acq_receipt_line_2_fiction_martigny, lib_martigny): """Test receipt properties.""" acre1 = acq_receipt_fiction_martigny acrl1 = acq_receipt_line_1_fiction_martigny acrl2 = acq_receipt_line_2_fiction_martigny # LIBRARY------------------------------------------------------------------ assert acre1.library_pid == lib_martigny.pid # ORGANISATION ------------------------------------------------------------ assert acre1.organisation_pid == lib_martigny.organisation_pid # ORDER ------------------------------------------------------------------- assert acre1.order_pid == acq_order_fiction_martigny.pid # NOTE -------------------------------------------------------------------- assert acre1.get_note(AcqReceiptNoteType.STAFF) # EXCHANGE_RATE ----------------------------------------------------------- assert acre1.exchange_rate # AMOUNT ------------------------------------------------------------------ adj_amount = sum(adj.get('amount') for adj in acre1.amount_adjustments) wished_amount = sum([acrl1.total_amount, acrl2.total_amount, adj_amount]) assert acre1.total_amount == wished_amount # QUANTITY ---------------------------------------------------------------- assert acre1.total_item_quantity == sum([acrl1.quantity, acrl2.quantity]) # ACQ ACCOUNT ------------------------------------------------------------- for amount in acre1.amount_adjustments: assert extracted_data_from_ref(amount.get('acq_account')) == \ acq_account_fiction_martigny.pid # RECEIPT LINES ----------------------------------------------------------- lines = [acrl1, acrl2] assert all(line in lines for line in acre1.get_receipt_lines()) lines_pid = [line.pid for line in lines] assert all(pid in lines_pid for pid in acre1.get_receipt_lines('pids')) assert acre1.get_receipt_lines('count') == 2
def test_patron_transaction_event_jsonresolver( patron_transaction_overdue_event_saxon): """Test patron transaction event json resolver.""" rec = Record.create({ 'patron_transaction_event': { '$ref': 'https://bib.rero.ch/api/patron_transaction_events/1' } }) assert extracted_data_from_ref(rec.get('patron_transaction_event')) == '1' # deleted record patron_transaction_overdue_event_saxon.delete() with pytest.raises(JsonRefError): rec.replace_refs().dumps() # non existing record rec = Record.create({ 'patron_transaction': { '$ref': 'https://bib.rero.ch/api/patron_transaction_events/n_e' } }) with pytest.raises(JsonRefError): rec.replace_refs().dumps()
def test_patron_pending_subscription(client, patron_type_grown_sion, patron_sion_no_email, librarian_sion_no_email, patron_transaction_overdue_event_martigny, lib_sion): """Test get pending subscription for patron.""" # At the beginning, `patron_sion_no_email` should have one pending # subscription. pending_subscription = patron_sion_no_email.get_pending_subscriptions() assert len(pending_subscription) == 1 # Pay this subscription. login_user_via_session(client, librarian_sion_no_email.user) post_entrypoint = 'invenio_records_rest.ptre_list' trans_pid = extracted_data_from_ref( pending_subscription[0]['patron_transaction'], data='pid') transaction = PatronTransaction.get_record_by_pid(trans_pid) payment = deepcopy(patron_transaction_overdue_event_martigny) del payment['pid'] payment['type'] = 'payment' payment['subtype'] = 'cash' payment['amount'] = transaction.total_amount payment['operator'] = { '$ref': get_ref_for_pid('patrons', librarian_sion_no_email.pid) } payment['library'] = {'$ref': get_ref_for_pid('libraries', lib_sion.pid)} payment['parent'] = pending_subscription[0]['patron_transaction'] res, _ = postdata(client, post_entrypoint, payment) assert res.status_code == 201 transaction = PatronTransaction.get_record_by_pid(transaction.pid) assert transaction.status == 'closed' # reload the patron and check the pending subscription. As we paid the # previous subscription, there will be none pending subscription patron_sion_no_email = Patron.get_record_by_pid(patron_sion_no_email.pid) pending_subscription = patron_sion_no_email.get_pending_subscriptions() assert len(pending_subscription) == 0
def loan_pid(self): """Shortcut for loan pid of the notification.""" return extracted_data_from_ref(self['context']['loan'])
def acq_order_pid(self): """Shortcut for acq order pid of the notification.""" return extracted_data_from_ref(self['context']['order'])
def update_items_locations_and_types(sender, record=None, **kwargs): """This method checks if the items of the parent record needs an update. This method checks the location and item_type of each item attached to the holding record and update the item record accordingly. This method should be connect with 'after_record_update'. :param record: the holding record. """ if not isinstance(record, Holding) and \ record.get('holdings_type') == HoldingTypes.SERIAL: # identify all items records attached to this serials holdings record # with different location and item_type. hold_circ_pid = record.circulation_category_pid hold_loc_pid = record.location_pid search = ItemsSearch().filter('term', holding__pid=record.pid) item_hits = search.\ filter('bool', should=[ Q('bool', must_not=[ Q('match', item_type__pid=hold_circ_pid)]), Q('bool', must_not=[ Q('match', location__pid=hold_loc_pid)])])\ .source(['pid']) items = [hit.meta.id for hit in item_hits.scan()] items_to_index = [] # update these items and make sure they have the same location/category # as the parent holdings record. for id in items: try: item = Item.get_record_by_id(id) if not item: continue items_to_index.append(id) item_temp_loc_pid, item_temp_type_pid = None, None # remove the item temporary_location if it is equal to the # new item location. temporary_location = item.get('temporary_location', {}) if temporary_location: item_temp_loc_pid = extracted_data_from_ref( temporary_location.get('$ref')) if hold_loc_pid != item.location_pid: if item_temp_loc_pid == hold_loc_pid: item.pop('temporary_location', None) item['location'] = { '$ref': get_ref_for_pid('locations', hold_loc_pid) } # remove the item temporary_item_type if it is equal to the # new item item_type. temporary_type = item.get('temporary_item_type', {}) if temporary_type: item_temp_type_pid = extracted_data_from_ref( temporary_type.get('$ref')) if hold_circ_pid != item.item_type_pid: if item_temp_type_pid == hold_circ_pid: item.pop('temporary_item_type', None) item['item_type'] = { '$ref': get_ref_for_pid('item_types', hold_circ_pid) } # update directly in database. db.session.query(item.model_cls).filter_by(id=item.id).update( {item.model_cls.json: item}) except Exception as err: pass if items_to_index: # commit session db.session.commit() # bulk indexing of item records. indexer = ItemsIndexer() indexer.bulk_index(items_to_index) process_bulk_queue.apply_async()
def populate_currency(record): """Add vendor currency to order data.""" vendor = record.get('vendor') if vendor: vendor = extracted_data_from_ref(vendor, data='record') record['currency'] = vendor.get('currency')