Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,11 @@ ways to better this mess
## better shtrove api experience

- better web-browsing experience
- when `Accept` header accepts html, use html regardless of query-params
- when query param `acceptMediatype` requests another mediatype, display on page in copy/pastable way
- exception: when given `withFileName`, download without html wrapping
- exception: `/trove/browse` should still give hypertext with clickable links
- include more explanatory docs (and better fill out those explanations)
- more helpful (less erratic) visual design
- even more helpful (less erratic) visual design
- in each html rendering of an api response, include a `<form>` for adding/editing/viewing query params
- in browsable html, replace json literals with rdf rendered like the rest of the page
- (perf) add bare-minimal IndexcardDeriver (iris, types, namelikes); use for search-result display
- better tsv/csv experience
- set default columns for `index-value-search` (and/or broadly improve `fields` handling)
- better turtle experience
Expand Down
12 changes: 7 additions & 5 deletions tests/share/search/index_strategy/_common_trovesearch_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,10 +117,10 @@ def test_cardsearch_pagination(self):
}))
self._index_indexcards(_cards)
# gather all pages results:
_querystring: str = f'page[size]={_page_size}'
_querystring: str | None = f'page[size]={_page_size}'
_result_iris: set[str] = set()
_page_count = 0
while True:
while _querystring is not None:
_cardsearch_handle = self.index_strategy.pls_handle_cardsearch(
CardsearchParams.from_querystring(_querystring),
)
Expand All @@ -133,9 +133,11 @@ def test_cardsearch_pagination(self):
_result_iris.update(_page_iris)
_page_count += 1
_next_cursor = _cardsearch_handle.cursor.next_cursor()
if _next_cursor is None:
break
_querystring = urlencode({'page[cursor]': _next_cursor.as_queryparam_value()})
_querystring = (
urlencode({'page[cursor]': _next_cursor.as_queryparam_value()})
if _next_cursor is not None
else None # done
)
self.assertEqual(_page_count, math.ceil(_total_count / _page_size))
self.assertEqual(_result_iris, _expected_iris)

Expand Down
11 changes: 5 additions & 6 deletions tests/share/test_oaipmh_trove.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,11 +232,9 @@ def _assert_full_list(self, verb, params, request_method, expected_count, page_s
pages = 0
count = 0
token = None
while True:
if token:
parsed = oai_request({'verb': verb, 'resumptionToken': token}, request_method)
else:
parsed = oai_request({'verb': verb, 'metadataPrefix': 'oai_dc', **params}, request_method)
next_params: dict[str, str] | None = {'verb': verb, 'metadataPrefix': 'oai_dc', **params}
while next_params is not None:
parsed = oai_request(next_params, request_method)
page = parsed.xpath('//oai:header/oai:identifier', namespaces=NAMESPACES)
pages += 1
count += len(page)
Expand All @@ -245,9 +243,10 @@ def _assert_full_list(self, verb, params, request_method, expected_count, page_s
token = token[0].text
if token:
assert len(page) == page_size
next_params = {'verb': verb, 'resumptionToken': token}
else:
assert len(page) <= page_size
break
next_params = None # done

assert count == expected_count
assert pages == math.ceil(expected_count / page_size)
6 changes: 3 additions & 3 deletions tests/trove/render/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

from trove.trovesearch.trovesearch_gathering import trovesearch_by_indexstrategy
from trove.render._base import BaseRenderer
from trove.render._rendering import ProtoRendering
from trove.render.rendering import ProtoRendering
from trove.vocab.namespaces import RDF
from tests.trove._input_output_tests import BasicInputOutputTestCase
from ._inputs import UNRENDERED_RDF, UNRENDERED_SEARCH_RDF, RdfCase
Expand Down Expand Up @@ -66,9 +66,9 @@ def assert_outputs_equal(self, expected_output, actual_output) -> None:
self._get_rendered_output(actual_output),
)

def _get_rendered_output(self, rendering: ProtoRendering):
def _get_rendered_output(self, rendering: ProtoRendering) -> str:
# for now, they always iter strings (update if/when bytes are in play)
return ''.join(rendering.iter_content()) # type: ignore[arg-type]
return ''.join(map(str, rendering.iter_content()))


class TrovesearchRendererTests(TroveRendererTests):
Expand Down
2 changes: 1 addition & 1 deletion tests/trove/render/test_jsonapi_renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from unittest import mock

from trove.render.jsonapi import RdfJsonapiRenderer
from trove.render._rendering import SimpleRendering
from trove.render.rendering import SimpleRendering
from trove.vocab.namespaces import BLARG
from . import _base

Expand Down
2 changes: 1 addition & 1 deletion tests/trove/render/test_jsonld_renderer.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import json

from trove.render.jsonld import RdfJsonldRenderer
from trove.render._rendering import SimpleRendering
from trove.render.rendering import SimpleRendering
from ._inputs import BLARG
from . import _base

Expand Down
2 changes: 1 addition & 1 deletion tests/trove/render/test_simple_csv_renderer.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from trove.render.simple_csv import TrovesearchSimpleCsvRenderer
from trove.render._rendering import SimpleRendering
from trove.render.rendering import SimpleRendering
from . import _base


Expand Down
2 changes: 1 addition & 1 deletion tests/trove/render/test_simple_json_renderer.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import json

from trove.render.simple_json import TrovesearchSimpleJsonRenderer
from trove.render._rendering import SimpleRendering
from trove.render.rendering import SimpleRendering
from trove.vocab.namespaces import BLARG
from . import _base

Expand Down
2 changes: 1 addition & 1 deletion tests/trove/render/test_simple_tsv_renderer.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from trove.render.simple_tsv import TrovesearchSimpleTsvRenderer
from trove.render._rendering import SimpleRendering
from trove.render.rendering import SimpleRendering
from . import _base


Expand Down
2 changes: 1 addition & 1 deletion tests/trove/render/test_turtle_renderer.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from primitive_metadata import primitive_rdf as rdf

from trove.render.turtle import RdfTurtleRenderer
from trove.render._rendering import SimpleRendering
from trove.render.rendering import SimpleRendering
from . import _base


Expand Down
2 changes: 2 additions & 0 deletions tests/trove/test_doctest.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import trove.util.frozen
import trove.util.iris
import trove.util.propertypath
import trove.vocab.mediatypes

_DOCTEST_OPTIONFLAGS = (
doctest.ELLIPSIS
Expand All @@ -15,6 +16,7 @@
trove.util.frozen,
trove.util.iris,
trove.util.propertypath,
trove.vocab.mediatypes,
)


Expand Down
11 changes: 4 additions & 7 deletions trove/render/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
from typing import Type

from django import http

from trove import exceptions as trove_exceptions
from trove.vocab.mediatypes import strip_mediatype_parameters
from ._base import BaseRenderer
from .jsonapi import RdfJsonapiRenderer
from .html_browse import RdfHtmlBrowseRenderer
Expand All @@ -25,10 +24,6 @@
TrovesearchSimpleTsvRenderer,
)

RendersType = Type[
BaseRenderer | RdfHtmlBrowseRenderer | RdfJsonapiRenderer | RdfTurtleRenderer | RdfJsonldRenderer | TrovesearchSimpleCsvRenderer | TrovesearchSimpleJsonRenderer | TrovesearchSimpleTsvRenderer
]

RENDERER_BY_MEDIATYPE = {
_renderer_type.MEDIATYPE: _renderer_type
for _renderer_type in RENDERERS
Expand All @@ -42,7 +37,9 @@ def get_renderer_type(request: http.HttpRequest) -> type[BaseRenderer]:
_requested_mediatype = request.GET.get('acceptMediatype')
if _requested_mediatype:
try:
_chosen_renderer_type = RENDERER_BY_MEDIATYPE[_requested_mediatype]
_chosen_renderer_type = RENDERER_BY_MEDIATYPE[
strip_mediatype_parameters(_requested_mediatype)
]
except KeyError:
raise trove_exceptions.CannotRenderMediatype(_requested_mediatype)
else:
Expand Down
6 changes: 3 additions & 3 deletions trove/render/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from trove.vocab import mediatypes
from trove.vocab.trove import TROVE_API_THESAURUS
from trove.vocab.namespaces import namespaces_shorthand
from ._rendering import ProtoRendering, SimpleRendering
from .rendering import ProtoRendering, SimpleRendering


@dataclasses.dataclass
Expand Down Expand Up @@ -61,15 +61,15 @@ def render_document(self) -> ProtoRendering:
except NotImplementedError:
raise NotImplementedError(f'class "{type(self)}" must implement either `render_document` or `simple_render_document`')
else:
return SimpleRendering( # type: ignore[return-value] # until ProtoRendering(typing.Protocol) with py3.12
return SimpleRendering(
mediatype=self.MEDIATYPE,
rendered_content=_content,
)

@classmethod
def render_error_document(cls, error: trove_exceptions.TroveError) -> ProtoRendering:
# may override, but default to jsonapi
return SimpleRendering( # type: ignore[return-value] # until ProtoRendering(typing.Protocol) with py3.12
return SimpleRendering(
mediatype=mediatypes.JSONAPI,
rendered_content=json.dumps(
{'errors': [{ # https://jsonapi.org/format/#error-objects
Expand Down
24 changes: 15 additions & 9 deletions trove/render/_html.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,20 @@
from xml.etree.ElementTree import (
Element,
SubElement,
tostring as etree_tostring,
)
from typing import Any

from primitive_metadata import primitive_rdf as rdf


__all__ = ('HtmlBuilder',)

HTML_DOCTYPE = '<!DOCTYPE html>'


@dataclasses.dataclass
class HtmlBuilder:
given_root: Element
given_root: Element = dataclasses.field(default_factory=lambda: Element('html'))
_: dataclasses.KW_ONLY
_nested_elements: list[Element] = dataclasses.field(default_factory=list)
_heading_depth: int = 0
Expand All @@ -36,18 +38,16 @@ def _current_element(self) -> Element:
# html-building helper methods

@contextlib.contextmanager
def nest_h_tag(self, **kwargs: Any) -> Generator[Element]:
def deeper_heading(self) -> Generator[str]:
_outer_heading_depth = self._heading_depth
if not _outer_heading_depth:
self._heading_depth = 1
elif _outer_heading_depth < 6: # h6 deepest
self._heading_depth += 1
_h_tag = f'h{self._heading_depth}'
with self.nest(_h_tag, **kwargs) as _nested:
try:
yield _nested
finally:
self._heading_depth = _outer_heading_depth
try:
yield f'h{self._heading_depth}'
finally:
self._heading_depth = _outer_heading_depth

@contextlib.contextmanager
def nest(self, tag_name: str, attrs: dict | None = None) -> Generator[Element]:
Expand All @@ -67,3 +67,9 @@ def leaf(self, tag_name: str, *, text: str | None = None, attrs: dict | None = N
_leaf_element.text = text.unicode_value
elif text is not None:
_leaf_element.text = text

def as_html_doc(self) -> str:
return '\n'.join((
HTML_DOCTYPE,
etree_tostring(self.root_element, encoding='unicode', method='html'),
))
47 changes: 0 additions & 47 deletions trove/render/_rendering.py

This file was deleted.

6 changes: 3 additions & 3 deletions trove/render/_simple_trovesearch.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from trove.vocab.jsonapi import JSONAPI_LINK_OBJECT
from trove.vocab.namespaces import TROVE, RDF
from ._base import BaseRenderer
from ._rendering import ProtoRendering, SimpleRendering
from .rendering import ProtoRendering, SimpleRendering
if TYPE_CHECKING:
from trove.util.json import JsonObject

Expand All @@ -30,7 +30,7 @@ def simple_multicard_rendering(self, cards: Iterator[tuple[str, JsonObject]]) ->
raise NotImplementedError

def unicard_rendering(self, card_iri: str, osfmap_json: JsonObject) -> ProtoRendering:
return SimpleRendering( # type: ignore[return-value]
return SimpleRendering(
mediatype=self.MEDIATYPE,
rendered_content=self.simple_unicard_rendering(card_iri, osfmap_json),
)
Expand All @@ -41,7 +41,7 @@ def multicard_rendering(self, card_pages: Iterator[dict[str, JsonObject]]) -> Pr
for _page in card_pages
for _card_iri, _card_contents in _page.items()
)
return SimpleRendering( # type: ignore[return-value]
return SimpleRendering(
mediatype=self.MEDIATYPE,
rendered_content=self.simple_multicard_rendering(_cards),
)
Expand Down
Loading