Source code for labapi.tree.search

"""Search result objects for LabArchives notebooks."""

from __future__ import annotations

from collections.abc import Iterator
from dataclasses import dataclass
from typing import TYPE_CHECKING, Any

from labapi.entry import Entry
from labapi.util import extract_etree

if TYPE_CHECKING:
    from .notebook import Notebook


[docs] @dataclass(frozen=True) class EntrySearchPage: """One page of LabArchives entry search results.""" page_size: int page_number: int total_found: int total_returned: int entries: tuple[Entry[Any], ...]
[docs] class EntrySearch: """Lazy entry search over a LabArchives notebook."""
[docs] def __init__(self, notebook: Notebook, query: str, *, page_size: int): """Initialize a notebook entry search.""" if page_size <= 0: raise ValueError("page_size must be positive") self._notebook = notebook self._query = query self._page_size = page_size
def __iter__(self) -> Iterator[EntrySearchPage]: """Iterate through search result pages until all results are exhausted.""" page_number = 0 while True: page = self.page(page_number) if page.total_returned == 0: return yield page returned_so_far = page.page_number * page.page_size + page.total_returned if returned_so_far >= page.total_found: return page_number += 1
[docs] def page(self, page_number: int) -> EntrySearchPage: """Return one zero-based search result page. :raises IndexError: If ``page_number`` is negative or out of range. """ if page_number < 0: raise IndexError("search page index must be non-negative") response = self._notebook.user.api_get( "search_tools/entry_search", nbid=self._notebook.id, query=self._query, page_size=self._page_size, page_number=page_number, entry_data=True, ) result_counts = extract_etree( response, {"results": {"total-found": int, "total-returned": int}}, ) entries: list[Entry[Any]] = [] for entry_element in response.findall("./entries/entry"): entry_fields = extract_etree(entry_element, {"eid": str, "part-type": str}) entry_data = entry_element.find("./entry-data") caption = entry_element.find("./caption") data = "" if ( entry_data is not None and entry_data.get("nil") != "true" and entry_data.text is not None ): data = entry_data.text elif ( caption is not None and caption.get("nil") != "true" and caption.text is not None ): data = caption.text entries.append( Entry.from_part_type( entry_fields["part-type"], entry_fields["eid"], data, self._notebook.user, ) ) page = EntrySearchPage( page_size=self._page_size, page_number=page_number, total_found=result_counts["total-found"], total_returned=result_counts["total-returned"], entries=tuple(entries), ) if page_number > 0 and page.total_returned == 0: raise IndexError(f"search page index {page_number} is out of range") return page