Example #1
0
    def _circulation_for_document_resolver(document_pid):
        """Return circulation info for the given document."""
        circulation = {}
        active_loans_count = 0
        loanable_items_count = 0
        items_count = 0
        item_pids = circulation_items_retriever(document_pid)

        for pid in item_pids:
            items_count += 1
            if not is_item_available(pid):
                active_loans_count += 1

            if circulation_is_item_available(pid):
                loanable_items_count += 1

        search = search_by_pid(
            document_pid=document_pid,
            filter_states=current_app.config.get(
                "CIRCULATION_STATES_LOAN_COMPLETED", []),
        )

        pending_loans = search_by_pid(document_pid=document_pid,
                                      filter_states=['PENDING'
                                                     ]).execute().hits.total

        circulation["number_of_past_loans"] = search.execute().hits.total
        circulation["number_of_items"] = items_count
        circulation["active_loans"] = active_loans_count
        circulation["loanable_items"] = loanable_items_count
        circulation["pending_loans"] = pending_loans
        circulation["overbooked"] = pending_loans > loanable_items_count
        return circulation
Example #2
0
    def delete(self, **kwargs):
        """Delete Document record."""
        loan_search_res = search_by_pid(
            document_pid=self["pid"],
            filter_states=['PENDING'] +
            current_app.config['CIRCULATION_STATES_LOAN_ACTIVE'],
        )
        if loan_search_res.count():
            raise RecordHasReferencesError(
                record_type="Document",
                record_id=self["pid"],
                ref_type="Loan",
                ref_ids=sorted([res["pid"] for res in loan_search_res.scan()]),
            )

        item_search = ItemSearch()
        item_search_res = item_search.search_by_document_pid(
            document_pid=self["pid"])
        if item_search_res.count():
            raise RecordHasReferencesError(
                record_type="Document",
                record_id=self["pid"],
                ref_type="Item",
                ref_ids=sorted([res["pid"] for res in item_search_res.scan()]),
            )
        return super(Document, self).delete(**kwargs)
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'
Example #4
0
def test_search_loans_by_pid_excluding_states(indexed_loans):
    """Test retrieve loan list belonging to an item excluding states."""
    search_result = search_by_pid(
        item_pid=dict(type="itemid", value="item_multiple_pending_on_loan_7"),
        exclude_states=["ITEM_ON_LOAN"],
    ).execute()
    _assert_total(search_result.hits.total, 2)
Example #5
0
 def ensure_item_can_be_updated(self, record):
     """Raises an exception if the item's status cannot be updated."""
     latest_version = record.revisions[-1]
     document_changed = False
     if latest_version:
         latest_version_document_pid = latest_version.get(
             "document_pid", None
         )
         document_changed = latest_version_document_pid != record.get(
             "document_pid", None
         )
     if document_changed:
         pid = record["pid"]
         item_pid = dict(value=pid, type=ITEM_PID_TYPE)
         if search_by_pid(
             item_pid=item_pid,
             filter_states=current_app.config[
                 "CIRCULATION_STATES_LOAN_ACTIVE"
             ]
             + current_app.config["CIRCULATION_STATES_LOAN_COMPLETED"]
             + current_app.config["CIRCULATION_STATES_LOAN_CANCELLED"],
         ).count():
             raise ItemHasPastLoansError(
                 "Not possible to update the document field if "
                 "the item already has past or active loans."
             )
Example #6
0
def test_search_loans_by_pid(indexed_loans):
    """Test retrieve loan list belonging to an item."""
    item_pid = dict(type="itemid", value="item_pending_1")
    loans = list(search_by_pid(item_pid=item_pid).scan())
    assert len(loans) == 1
    loan = Loan.get_record_by_pid(loans[0]["pid"])
    assert loan["item_pid"] == item_pid
Example #7
0
 def get_past_loans_by_doc_pid(self, document_pid):
     """Return any past loans for the given document."""
     return search_by_pid(
         document_pid=document_pid,
         filter_states=current_app.config.get(
             "CIRCULATION_STATES_LOAN_COMPLETED", []),
     )
Example #8
0
 def get_overdue_loans_by_doc_pid(self, document_pid):
     """Return any overdue loans for the given document."""
     return search_by_pid(
         document_pid=document_pid,
         filter_states=current_app.config.get(
             "CIRCULATION_STATES_LOAN_ACTIVE", []),
     ).filter('range', end_date=dict(lt='now/d'))
def get_active_loan_by_item_pid(item_pid):
    """Return any active loans for the given item."""
    return search_by_pid(
        item_pid=item_pid,
        filter_states=current_app.config.get("CIRCULATION_STATES_LOAN_ACTIVE",
                                             []),
    )
Example #10
0
 def get_active_loans_by_doc_pid(self, document_pid):
     """Return any active loans for the given document."""
     return search_by_pid(
         document_pid=document_pid,
         filter_states=current_app.config.get(
             "CIRCULATION_STATES_LOAN_ACTIVE", []),
     )
Example #11
0
def get_loan_next_available_date(document_pid):
    """Return active loans on document sorted by the earliest end date."""
    return search_by_pid(
        document_pid=document_pid,
        filter_states=current_app.config.get("CIRCULATION_STATES_LOAN_ACTIVE",
                                             []),
        sort_by_field="end_date",
    )
Example #12
0
def test_search_loans_by_pid_filtering_states(indexed_loans):
    """Test retrieve loan list belonging to an item filtering states."""
    search = search_by_pid(
        item_pid=dict(type="itemid", value="item_multiple_pending_on_loan_7"),
        filter_states=["PENDING", "ITEM_ON_LOAN"],
    )
    search_result = search.execute()
    _assert_total(search_result.hits.total, 3)
Example #13
0
 def get_number_of_loans(self):
     """Get number of document loans."""
     search = search_by_pid(document_pid=self.pid,
                            exclude_states=[
                                'CANCELLED',
                                'ITEM_RETURNED',
                            ])
     return search.source().count()
Example #14
0
 def get_number_of_loans(self):
     """Get number of loans."""
     search = search_by_pid(item_pid=self.pid,
                            exclude_states=[
                                'CANCELLED',
                                'ITEM_RETURNED',
                            ])
     results = search.source().count()
     return results
Example #15
0
 def get_loan_pid_with_item_on_loan(cls, item_pid):
     """Returns loan pid for checked out item."""
     search = search_by_pid(
         item_pid=item_pid, filter_states=['ITEM_ON_LOAN'])
     results = search.source(['pid']).scan()
     try:
         return next(results).pid
     except StopIteration:
         return None
Example #16
0
File: api.py Project: jma/rero-ils
 def get_number_of_loans(self):
     """Get number of document loans."""
     from ..loans.api import LoanState
     search = search_by_pid(document_pid=self.pid,
                            exclude_states=[
                                LoanState.CANCELLED,
                                LoanState.ITEM_RETURNED,
                            ])
     return search.source().count()
Example #17
0
def index_loans_after_item_indexed(item_pid):
    """Index loan to refresh item reference."""
    loan_search = search_by_pid(item_pid=item_pid)
    loan_ids = []
    for loan in loan_search.scan():
        record = Loan.get_record_by_pid(loan[Loan.pid_field])
        if record:
            loan_ids.append(record.id)

    RecordIndexer().bulk_index(loan_ids)
Example #18
0
 def item_has_active_loan_or_request(self):
     """Return True if active loan or a request found for item."""
     states = ['PENDING'] + \
         current_app.config['CIRCULATION_STATES_LOAN_ACTIVE']
     search = search_by_pid(
         item_pid=self.pid,
         filter_states=states,
     )
     search_result = search.execute()
     return search_result.hits.total
Example #19
0
 def get_loan_pid_with_item_in_transit(cls, item_pid):
     """Returns loan pi for in_transit item."""
     search = search_by_pid(
         item_pid=item_pid, filter_states=[
             "ITEM_IN_TRANSIT_FOR_PICKUP",
             "ITEM_IN_TRANSIT_TO_HOUSE"])
     results = search.source(['pid']).scan()
     try:
         return next(results).pid
     except StopIteration:
         return None
Example #20
0
 def delete(self, **kwargs):
     """Delete Item record."""
     loan_search_res = search_by_pid(
         item_pid=self["pid"],
         filter_states=current_app.config['CIRCULATION_STATES_LOAN_ACTIVE'])
     if loan_search_res.count():
         raise RecordHasReferencesError(
             record_type='Item',
             record_id=self["pid"],
             ref_type='Loan',
             ref_ids=sorted([res["pid"] for res in loan_search_res.scan()]))
     return super(Item, self).delete(**kwargs)
Example #21
0
 def get_requests(self):
     """Return any pending, item_on_transit, item_at_desk loans."""
     search = search_by_pid(
         item_pid=self.pid, filter_states=[
             'PENDING',
             'ITEM_AT_DESK',
             'ITEM_IN_TRANSIT_FOR_PICKUP'
         ]).params(preserve_order=True)\
         .source(['pid'])\
         .sort({'transaction_date': {'order': 'asc'}})
     for result in search.scan():
         yield Loan.get_record_by_pid(result.pid)
Example #22
0
File: api.py Project: jma/rero-ils
def search_active_loans_for_item(item_pid):
    """Return count and all active loans for an item."""
    item_pid_object = item_pid_to_object(item_pid)
    states = ['PENDING'] + \
        current_app.config['CIRCULATION_STATES_LOAN_ACTIVE']
    search = search_by_pid(item_pid=item_pid_object,
                           filter_states=states,
                           sort_by_field='_created',
                           sort_order='desc')
    loans_count = search.count()
    try:
        return loans_count, search.scan()
    except StopIteration:
        return loans_count
Example #23
0
 def delete(self, **kwargs):
     """Delete Item record."""
     item_pid = dict(type=ITEM_PID_TYPE, value=self["pid"])
     loan_search_res = search_by_pid(
         item_pid=item_pid,
         filter_states=current_app.config["CIRCULATION_STATES_LOAN_ACTIVE"],
     )
     if loan_search_res.count():
         raise RecordHasReferencesError(
             record_type="Item",
             record_id=self["pid"],
             ref_type="Loan",
             ref_ids=sorted([res["pid"] for res in loan_search_res.scan()]),
         )
     return super().delete(**kwargs)
Example #24
0
    def get_requests(self, sort_by=None):
        """Return sorted pending, item_on_transit, item_at_desk loans.

        default sort is transaction_date.
        """
        search = search_by_pid(item_pid=self.pid,
                               filter_states=[
                                   'PENDING', 'ITEM_AT_DESK',
                                   'ITEM_IN_TRANSIT_FOR_PICKUP'
                               ]).params(preserve_order=True).source(['pid'])
        order_by = 'asc'
        sort_by = sort_by or 'transaction_date'
        if sort_by.startswith('-'):
            sort_by = sort_by[1:]
            order_by = 'desc'
        search = search.sort({sort_by: {'order': order_by}})
        for result in search.scan():
            yield Loan.get_record_by_pid(result.pid)
Example #25
0
    def delete(self, **kwargs):
        """Delete Document record."""
        loan_search_res = search_by_pid(
            document_pid=self["pid"],
            filter_states=["PENDING"] +
            current_app.config["CIRCULATION_STATES_LOAN_ACTIVE"],
        )
        if loan_search_res.count():
            raise RecordHasReferencesError(
                record_type="Document",
                record_id=self["pid"],
                ref_type="Loan",
                ref_ids=sorted([res["pid"] for res in loan_search_res.scan()]),
            )

        item_search = current_app_ils.item_search_cls()
        item_search_res = item_search.search_by_document_pid(
            document_pid=self["pid"])
        if item_search_res.count():
            raise RecordHasReferencesError(
                record_type="Document",
                record_id=self["pid"],
                ref_type="Item",
                ref_ids=sorted([res["pid"] for res in item_search_res.scan()]),
            )

        req_search = current_app_ils.document_request_search_cls()
        req_search_res = req_search.search_by_document_pid(
            document_pid=self["pid"])
        if req_search_res.count():
            raise RecordHasReferencesError(
                record_type="Document",
                record_id=self["pid"],
                ref_type="DocumentRequest",
                ref_ids=sorted([res["pid"] for res in req_search_res.scan()]),
            )

        return super().delete(**kwargs)
Example #26
0
def test_search_loans_by_pid_excluding_states(indexed_loans):
    """Test retrieve loan list belonging to an item excluding states."""
    search_result = search_by_pid(item_pid="item_multiple_pending_on_loan_7",
                                  exclude_states=["ITEM_ON_LOAN"]).execute()
    assert search_result.hits.total == 2
Example #27
0
def get_overdue_loans_by_doc_pid(document_pid):
    """Return any overdue loans for the given document."""
    states = current_app.config["CIRCULATION_STATES_LOAN_ACTIVE"]
    return search_by_pid(document_pid=document_pid,
                         filter_states=states).filter(
                             "range", end_date=dict(lt="now/d"))
def test_search_loans_by_pid(indexed_loans):
    """Test retrieve loan list belonging to an item."""
    loans = list(search_by_pid(item_pid="item_pending_1").scan())
    assert len(loans) == 1
    loan = Loan.get_record_by_pid(loans[0][Loan.pid_field])
    assert loan.get("item_pid") == "item_pending_1"
def test_search_loans_by_pid_filtering_states(indexed_loans):
    """Test retrieve loan list belonging to an item filtering states."""
    search = search_by_pid(item_pid="item_multiple_pending_on_loan_7",
                           filter_states=["PENDING", "ITEM_ON_LOAN"])
    search_result = search.execute()
    assert search_result.hits.total == 3
Example #30
0
    def get_links_to_me(self, get_pids=False):
        """Record links.

        :param get_pids: if True list of linked pids
                         if False count of linked records
        """
        links = {}
        from ..holdings.api import HoldingsSearch
        from ..items.api import ItemsSearch
        from ..loans.models import LoanState
        hold_query = HoldingsSearch().filter('term', document__pid=self.pid)
        item_query = ItemsSearch().filter('term', document__pid=self.pid)
        loan_query = search_by_pid(
            document_pid=self.pid,
            exclude_states=[LoanState.CANCELLED, LoanState.ITEM_RETURNED])
        acq_order_lines_query = AcqOrderLinesSearch() \
            .filter('term', document__pid=self.pid)
        relation_types = {
            'partOf': 'partOf.document.pid',
            'supplement': 'supplement.pid',
            'supplementTo': 'supplementTo.pid',
            'otherEdition': 'otherEdition.pid',
            'otherPhysicalFormat': 'otherPhysicalFormat.pid',
            'issuedWith': 'issuedWith.pid',
            'precededBy': 'precededBy.pid',
            'succeededBy': 'succeededBy.pid',
            'relatedTo': 'relatedTo.pid',
            'hasReproduction': 'hasReproduction.pid',
            'reproductionOf': 'reproductionOf.pid'
        }

        if get_pids:
            holdings = sorted_pids(hold_query)
            items = sorted_pids(item_query)
            loans = sorted_pids(loan_query)
            acq_order_lines = sorted_pids(acq_order_lines_query)
            documents = {}
            for relation, relation_es in relation_types.items():
                doc_query = DocumentsSearch() \
                    .filter({'term': {relation_es: self.pid}})
                pids = sorted_pids(doc_query)
                if pids:
                    documents[relation] = pids
        else:
            holdings = hold_query.count()
            items = item_query.count()
            loans = loan_query.count()
            acq_order_lines = acq_order_lines_query.count()
            documents = 0
            for relation, relation_es in relation_types.items():
                doc_query = DocumentsSearch() \
                    .filter({'term': {relation_es: self.pid}})
                documents += doc_query.count()
        if holdings:
            links['holdings'] = holdings
        if items:
            links['items'] = items
        if loans:
            links['loans'] = loans
        if acq_order_lines:
            links['acq_order_lines'] = acq_order_lines
        if documents:
            links['documents'] = documents
        return links
def test_search_loans_by_pid_excluding_states(indexed_loans):
    """Test retrieve loan list belonging to an item excluding states."""
    search_result = search_by_pid(item_pid="item_multiple_pending_on_loan_7",
                                  exclude_states=["ITEM_ON_LOAN"]).execute()
    assert search_result.hits.total == 2
Example #32
0
def test_search_loans_by_pid_filtering_states(indexed_loans):
    """Test retrieve loan list belonging to an item filtering states."""
    search = search_by_pid(item_pid="item_multiple_pending_on_loan_7",
                           filter_states=["PENDING", "ITEM_ON_LOAN"])
    search_result = search.execute()
    assert search_result.hits.total == 3
Example #33
0
def test_search_loans_by_pid(indexed_loans):
    """Test retrieve loan list belonging to an item."""
    loans = list(search_by_pid(item_pid="item_pending_1").scan())
    assert len(loans) == 1
    loan = Loan.get_record_by_pid(loans[0]["pid"])
    assert loan.get("item_pid") == "item_pending_1"