Skip to content

Commit

Permalink
search: add facets for inventory list
Browse files Browse the repository at this point in the history
* Adds temporary item type facet.
* Adds temporary location facet.
* Adds the checkout_date and the due_date fields to the export.
* Closes rero#2364.
* Closes rero#2611.

Co-Authored-by: Laurent Dubois <laurent.dubois@itld-solutions.be>
lauren-d committed Feb 4, 2022
1 parent 9f07638 commit 10a55b1
Showing 7 changed files with 110 additions and 35 deletions.
14 changes: 14 additions & 0 deletions rero_ils/config.py
Original file line number Diff line number Diff line change
@@ -1974,6 +1974,16 @@ def _(x):
field='item_type.pid',
size=RERO_ILS_DEFAULT_AGGREGATION_SIZE)
),
temporary_location=dict(
terms=dict(
field='temporary_location.pid',
size=RERO_ILS_DEFAULT_AGGREGATION_SIZE)
),
temporary_item_type=dict(
terms=dict(
field='temporary_item_type.pid',
size=RERO_ILS_DEFAULT_AGGREGATION_SIZE)
),
status=dict(
terms=dict(
field='status',
@@ -1999,6 +2009,10 @@ def _(x):
_('library'): and_term_filter('library.pid'),
_('location'): and_term_filter('location.pid'),
_('item_type'): and_term_filter('item_type.pid'),
_('temporary_item_type'):
and_term_filter('temporary_item_type.pid'),
_('temporary_location'):
and_term_filter('temporary_location.pid'),
_('status'): and_term_filter('status'),
_('issue_status'): and_term_filter('issue.status'),
_('vendor'): and_term_filter('vendor.pid'),
2 changes: 2 additions & 0 deletions rero_ils/modules/items/serializers/__init__.py
Original file line number Diff line number Diff line change
@@ -48,6 +48,8 @@
'checkin_note',
'checkout_note',
'loans_count',
'checkout_date',
'due_date',
'last_transaction_date',
'status',
'created',
16 changes: 12 additions & 4 deletions rero_ils/modules/items/serializers/csv.py
Original file line number Diff line number Diff line change
@@ -30,6 +30,7 @@
from rero_ils.modules.item_types.api import ItemTypesSearch
from rero_ils.modules.libraries.api import LibrariesSearch
from rero_ils.modules.loans.api import LoansSearch
from rero_ils.modules.loans.models import LoanState
from rero_ils.modules.locations.api import LocationsSearch
from rero_ils.utils import get_i18n_supported_languages

@@ -160,12 +161,13 @@ def _build_doc(data):

def get_loans_by_item_pids(item_pids):
"""Get loans for the given item pid list."""
states = ['PENDING'] + \
states = \
current_app.config['CIRCULATION_STATES_LOAN_ACTIVE']
loan_search = LoansSearch() \
.filter('terms', state=states) \
.filter('terms', item_pid__value=item_pids) \
.source(['pid', 'item_pid.value', '_created'])
.source(['pid', 'item_pid.value', 'start_date',
'end_date', 'state', '_created'])
agg = A('terms', field='item_pid.value', size=chunk_size)
loan_search.aggs.bucket('loans_count', agg)

@@ -179,8 +181,9 @@ def get_loans_by_item_pids(item_pids):
}
}
)

results = loan_search.execute()
# default results size for the execute method is 10.
# We need to set this to the chunk size"
results = loan_search[0:chunk_size].execute()
agg_buckets = {}
for result in results.aggregations.loans_count.buckets:
agg_buckets[result.key] = result.doc_count
@@ -195,6 +198,11 @@ def get_loans_by_item_pids(item_pids):
'last_transaction_date': ciso8601.parse_datetime(
loan_data['_created']).date()
}
if loan_data.get('state') == LoanState.ITEM_ON_LOAN:
loans[item_pid]['checkout_date'] = ciso8601.\
parse_datetime(loan_data['start_date']).date()
loans[item_pid]['due_date'] = ciso8601.\
parse_datetime(loan_data['end_date']).date()
return loans

headers = dict.fromkeys(self.csv_included_fields)
51 changes: 24 additions & 27 deletions rero_ils/modules/items/serializers/json.py
Original file line number Diff line number Diff line change
@@ -21,14 +21,14 @@
from rero_ils.modules.documents.api import search_document_by_pid
from rero_ils.modules.documents.utils import filter_document_type_buckets, \
title_format_text_head
from rero_ils.modules.item_types.api import ItemType
from rero_ils.modules.item_types.api import ItemTypesSearch
from rero_ils.modules.items.api import Item
from rero_ils.modules.items.models import ItemStatus
from rero_ils.modules.libraries.api import Library
from rero_ils.modules.locations.api import Location
from rero_ils.modules.libraries.api import LibrariesSearch, Library
from rero_ils.modules.locations.api import Location, LocationsSearch
from rero_ils.modules.organisations.api import Organisation
from rero_ils.modules.serializers import JSONSerializer
from rero_ils.modules.vendors.api import Vendor
from rero_ils.modules.vendors.api import VendorsSearch


class ItemsJSONSerializer(JSONSerializer):
@@ -44,6 +44,26 @@ def post_process_serialize_search(self, results, pid_fetcher):
orgs = {}
libs = {}
locs = {}

# enrich library bucket
JSONSerializer.enrich_bucket_with_data(
results, 'library', LibrariesSearch, 'name')
# enrich location bucket
JSONSerializer.enrich_bucket_with_data(
results, 'location', LocationsSearch, 'name')
# enrich item type bucket
JSONSerializer.enrich_bucket_with_data(
results, 'item_type', ItemTypesSearch, 'name')
# enrich temporary item type bucket
JSONSerializer.enrich_bucket_with_data(
results, 'temporary_item_type', ItemTypesSearch, 'name')
# enrich temporary location bucket
JSONSerializer.enrich_bucket_with_data(
results, 'temporary_location', LocationsSearch, 'name')
# enrich vendor bucket
JSONSerializer.enrich_bucket_with_data(
results, 'vendor', VendorsSearch, 'name')

for record in records:
metadata = record.get('metadata', {})
document = search_document_by_pid(
@@ -100,29 +120,6 @@ def post_process_serialize_search(self, results, pid_fetcher):
.get_record_by_pid(location['pid'])
location['name'] = locs[location['pid']].get('name')

# Add library name
for lib_term in results.get('aggregations', {}).get(
'library', {}).get('buckets', []):
lib = Library.get_record_by_pid(lib_term.get('key'))
lib_term['name'] = lib.get('name')
# Add location name
for loc_term in results.get('aggregations', {}).get(
'location', {}).get('buckets', []):
loc = Location.get_record_by_pid(loc_term.get('key'))
loc_term['name'] = loc.get('name')

# Add item type name
for item_type_term in results.get('aggregations', {}).get(
'item_type', {}).get('buckets', []):
item_type = ItemType.get_record_by_pid(item_type_term.get('key'))
item_type_term['name'] = item_type.get('name')

# Add vendor name
for vendor_term in results.get('aggregations', {}).get(
'vendor', {}).get('buckets', []):
vendor = Vendor.get_record_by_pid(vendor_term.get('key'))
vendor_term['name'] = vendor.get('name')

# Correct document type buckets
if results.get('aggregations', {}).get('document_type'):
buckets = results['aggregations']['document_type']['buckets']
34 changes: 34 additions & 0 deletions rero_ils/modules/serializers.py
Original file line number Diff line number Diff line change
@@ -122,6 +122,40 @@ def complete_bucket_with_attribute(results, bucket_name, resource_cls,
if attr in resource:
term[attr] = resource.get(attr)

@staticmethod
def enrich_bucket_with_data(results, bucket_name, search_cls,
attributes_name):
"""Complete a bucket by adding new keys based on resource attributes.
:param results: the ES results data (containing aggregations).
:param bucket_name: the bucket name to perform.
:param search_cls: the related search class.
:param attributes_name: attributes to load from search data.
"""
attributes_name = attributes_name or []
if not isinstance(attributes_name, list):
attributes_name = [attributes_name]
buckets = results.get('aggregations', {}) \
.get(bucket_name, {}).get('buckets', [])

# extract pids from buckets
bucket_pids = [term['key'] for term in buckets]

# search all pids with search class
query = search_cls() \
.filter('terms', pid=list(bucket_pids)) \
.source(['pid'] + attributes_name)

# preprocess results
search_data = {result.pid: result.to_dict() for result in
query.scan()}

# complete bukets with data
for term in buckets:
for attr in attributes_name:
if attr in search_data[term['key']]:
term[attr] = search_data[term['key']].get(attr)


json_v1 = JSONSerializer(RecordSchemaJSONV1)
"""JSON v1 serializer."""
20 changes: 20 additions & 0 deletions tests/api/items/test_items_rest.py
Original file line number Diff line number Diff line change
@@ -1010,3 +1010,23 @@ def test_item_possible_actions(client, item_lib_martigny,
reindex=True
)
assert circ_policy.can_checkout


def test_items_facets(
client,
item_lib_martigny, # on shelf
item_lib_fully, # on loan
rero_json_header
):
"""Test record retrieval."""
list_url = url_for('invenio_records_rest.item_list')
response = client.get(list_url, headers=rero_json_header)
assert response.status_code == 200
data = get_json(response)
aggs = data['aggregations']
# check all facets are present
for facet in [
'document_type', 'issue_status', 'item_type', 'library', 'location',
'status', 'temporary_item_type', 'temporary_location', 'vendor'
]:
assert aggs[facet]
8 changes: 4 additions & 4 deletions tests/api/test_serializers.py
Original file line number Diff line number Diff line change
@@ -158,10 +158,10 @@ def test_items_serializers(
'"location_name","barcode","call_number","second_call_number",' \
'"enumerationAndChronology","item_type","temporary_item_type",' \
'"temporary_item_type_end_date","general_note","staff_note",' \
'"checkin_note","checkout_note","loans_count",' \
'"last_transaction_date","status","created","issue_status",' \
'"issue_status_date","issue_claims_count","issue_expected_date",' \
'"issue_regular"' in data
'"checkin_note","checkout_note","loans_count","checkout_date",' \
'"due_date","last_transaction_date","status","created",' \
'"issue_status","issue_status_date","issue_claims_count",' \
'"issue_expected_date","issue_regular"' in data


def test_loans_serializers(

0 comments on commit 10a55b1

Please sign in to comment.