diff --git a/requirements.txt b/requirements.txt index 82774c6ab..bee50f6de 100644 --- a/requirements.txt +++ b/requirements.txt @@ -43,4 +43,4 @@ xmltodict==0.12.0 # MIT # Allows custom-rendered IDs, hiding null values, and including data in error responses git+https://github.com/cos-forks/django-rest-framework-json-api.git@v4.2.1+cos0 -git+https://github.com/aaxelb/primitive_metadata.git@0.2024.13 +git+https://github.com/aaxelb/primitive_metadata.git@0.2024.14 diff --git a/trove/trovesearch/page_cursor.py b/trove/trovesearch/page_cursor.py index 646c09bbd..33aa7f8f6 100644 --- a/trove/trovesearch/page_cursor.py +++ b/trove/trovesearch/page_cursor.py @@ -83,10 +83,15 @@ class OffsetCursor(PageCursor): start_offset: int = 0 def is_valid(self) -> bool: + _end_offset = ( + self.total_count + if self.bounded_page_size == self.page_size + else min(self.total_count, self.page_size) + ) return ( super().is_valid() and 0 <= self.start_offset <= MAX_OFFSET - and self.start_offset < self.total_count + and self.start_offset < _end_offset ) def is_first_page(self) -> bool: diff --git a/trove/trovesearch/search_handle.py b/trove/trovesearch/search_handle.py index 8bd0c262d..69c3d333a 100644 --- a/trove/trovesearch/search_handle.py +++ b/trove/trovesearch/search_handle.py @@ -27,13 +27,8 @@ def total_result_count(self) -> primitive_rdf.Literal: else self.cursor.total_count ) - def get_next_streaming_handle(self): - _next_params = self.search_params.get_next_streaming_params() - return ( - self.handler(_next_params) - if (_next_params is not None) and (self.handler is not None) - else None - ) + def get_next_streaming_handle(self) -> typing.Self | None: + raise NotImplementedError @dataclasses.dataclass @@ -64,6 +59,19 @@ def __post_init__(self): _cursor.first_page_ids = [_result.card_id for _result in _page] return _page + def get_next_streaming_handle(self) -> typing.Self | None: + breakpoint() + _next_cursor = self.cursor.next_cursor() + if (_next_cursor is not None) and (self.handler is not None): + _next_params = dataclasses.replace( + self.search_params, + page_cursor=_next_cursor, + include=frozenset([(TROVE.searchResultPage,)]), + ) + if self.handler is not None: + return self.handler(_next_params) + return None + @dataclasses.dataclass class ValuesearchHandle(BasicSearchHandle): diff --git a/trove/trovesearch/search_params.py b/trove/trovesearch/search_params.py index 6670c1885..027ed5756 100644 --- a/trove/trovesearch/search_params.py +++ b/trove/trovesearch/search_params.py @@ -172,9 +172,6 @@ def _gather_include(cls, queryparams: QueryparamDict): for _, _include_value in queryparams.get('include', []) ) - def get_next_streaming_params(self) -> typing.Self | None: - return None - @dataclasses.dataclass(frozen=True) class Textsegment: @@ -558,20 +555,6 @@ def to_querydict(self) -> QueryDict: _querydict['indexStrategy'] = self.index_strategy_name return _querydict - def get_next_streaming_params(self) -> typing.Self | None: - _next_diff = self._streaming_next_diff_kwargs() - return ( - None - if _next_diff.get('cursor') is None - else dataclasses.replace(self, **_next_diff) - ) - - def _streaming_next_diff_kwargs(self) -> dict: - return { - 'cursor': self.page_cursor.next_cursor(), - 'include': frozenset([(TROVE.searchResultPage,)]), - } - @dataclasses.dataclass(frozen=True) class ValuesearchParams(CardsearchParams): diff --git a/trove/trovesearch/trovesearch_gathering.py b/trove/trovesearch/trovesearch_gathering.py index fb66bbc21..974bedbc5 100644 --- a/trove/trovesearch/trovesearch_gathering.py +++ b/trove/trovesearch/trovesearch_gathering.py @@ -1,6 +1,7 @@ import dataclasses import logging import urllib.parse +from typing import ClassVar from primitive_metadata.primitive_rdf import ( Literal, @@ -72,16 +73,15 @@ class _TypedFocus(gather.Focus): - # TYPE_IRI: str (expected on subclasses) - # ADDITIONAL_TYPE_IRIS: Iterable[str] (optional on subclasses) + TYPE_IRI: ClassVar[str] # (expected on subclasses) + ADDITIONAL_TYPE_IRIS: ClassVar[tuple[str, ...]] = () # (optional on subclasses) @classmethod - def new(cls, *args, type_iris=(), **kwargs): + def new(cls, *, type_iris=(), **kwargs): return super().new( - *args, # add type_iri to new Focus instance type_iris={ - cls.TYPE_IRI + cls.TYPE_IRI, *getattr(cls, 'ADDITIONAL_TYPE_IRIS', ()), *type_iris }, @@ -89,38 +89,22 @@ def new(cls, *args, type_iris=(), **kwargs): ) +@dataclasses.dataclass(frozen=True) class CardsearchFocus(_TypedFocus): TYPE_IRI = TROVE.Cardsearch - # additional namedtuple fields - search_params: CardsearchParams - search_handle: CardsearchHandle - - @classmethod - def new(cls, *args, search_params, search_handle, **kwargs): - _base = super().new(*args, **kwargs) - return cls( - *_base, - search_params=search_params, - search_handle=search_handle, - ) + # additional dataclass fields + search_params: CardsearchParams = dataclasses.field(compare=False) + search_handle: CardsearchHandle = dataclasses.field(compare=False) +@dataclasses.dataclass(frozen=True) class ValuesearchFocus(_TypedFocus): TYPE_IRI = TROVE.Valuesearch - # additional namedtuple fields - search_params: ValuesearchParams - search_handle: ValuesearchHandle - - @classmethod - def new(cls, *args, search_params, search_handle, **kwargs): - _base = super().new(*args, **kwargs) - return cls( - *_base, - search_params=search_params, - search_handle=search_handle, - ) + # additional dataclass fields + search_params: ValuesearchParams = dataclasses.field(compare=False) + search_handle: ValuesearchHandle = dataclasses.field(compare=False) class IndexcardFocus(_TypedFocus): @@ -347,7 +331,6 @@ def _load_card_descriptions_derived(card_iris, deriver_iri: str) -> dict[str, rd ) .select_related('upriver_indexcard') .prefetch_related('upriver_indexcard__focus_identifier_set') - .get() ) _by_card_iri = {} for _derived in _derived_indexcard_qs: diff --git a/trove/views/search.py b/trove/views/search.py index 408b7010f..573133566 100644 --- a/trove/views/search.py +++ b/trove/views/search.py @@ -54,14 +54,13 @@ def get(self, request): _search_params = self._parse_search_params(request) _specific_index = index_strategy.get_index_for_trovesearch(_search_params) _focus = self.focus_type.new( - _url, - self.focus_type.TYPE_IRI, + iris=_url, search_params=_search_params, search_handle=self.get_search_handle(_specific_index, _search_params), ) if _renderer_type.PASSIVE_RENDER: # fill the gathering's cache with requested info - self._gather_as_requested(_search_gathering, _focus) + _search_gathering.ask(_search_params.include, focus=_focus) # take gathered data into a response _renderer = _renderer_type(_focus, _search_gathering) return make_http_response( @@ -86,10 +85,7 @@ def _start_gathering(self, renderer_type) -> gather.Gathering: }) def get_search_handle(self, specific_index, search_params) -> BasicSearchHandle: - _handler = self.get_search_handler(specific_index) - _handle = _handler(search_params) - _handle.handler = _handler - return _handle + return self._get_wrapped_handler(specific_index)(search_params) def get_search_handler( self, @@ -97,9 +93,14 @@ def get_search_handler( ) -> _TrovesearchHandler: raise NotImplementedError - def _gather_as_requested(self, gathering, focus) -> None: - _search_params: BaseTroveParams = gathering.gatherer_kwargs['search_params'] - gathering.ask(_search_params.include) + def _get_wrapped_handler(self, specific_index): + _raw_handler = self.get_search_handler(specific_index) + + def _wrapped_handler(search_params): + _handle = _raw_handler(search_params) + _handle.handler = _wrapped_handler + return _handle + return _wrapped_handler class CardsearchView(_BaseTrovesearchView):