oarepo-runtime 2.0.0.dev38__py3-none-any.whl → 2.0.0.dev40__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- oarepo_runtime/__init__.py +1 -1
 - oarepo_runtime/api.py +28 -0
 - oarepo_runtime/ext.py +11 -2
 - oarepo_runtime/resources/signposting/__init__.py +365 -0
 - {oarepo_runtime-2.0.0.dev38.dist-info → oarepo_runtime-2.0.0.dev40.dist-info}/METADATA +2 -1
 - {oarepo_runtime-2.0.0.dev38.dist-info → oarepo_runtime-2.0.0.dev40.dist-info}/RECORD +9 -8
 - {oarepo_runtime-2.0.0.dev38.dist-info → oarepo_runtime-2.0.0.dev40.dist-info}/WHEEL +0 -0
 - {oarepo_runtime-2.0.0.dev38.dist-info → oarepo_runtime-2.0.0.dev40.dist-info}/entry_points.txt +0 -0
 - {oarepo_runtime-2.0.0.dev38.dist-info → oarepo_runtime-2.0.0.dev40.dist-info}/licenses/LICENSE +0 -0
 
    
        oarepo_runtime/__init__.py
    CHANGED
    
    
    
        oarepo_runtime/api.py
    CHANGED
    
    | 
         @@ -153,6 +153,7 @@ class Model[ 
     | 
|
| 
       153 
153 
     | 
    
         
             
                    model_metadata: ModelMetadata | None = None,
         
     | 
| 
       154 
154 
     | 
    
         
             
                    features: Mapping[str, Any] | None = None,
         
     | 
| 
       155 
155 
     | 
    
         
             
                    imports: list[Import] | None = None,
         
     | 
| 
      
 156 
     | 
    
         
            +
                    ui_blueprint_name: str | None = None,
         
     | 
| 
       156 
157 
     | 
    
         
             
                ):
         
     | 
| 
       157 
158 
     | 
    
         
             
                    """Initialize the model configuration.
         
     | 
| 
       158 
159 
     | 
    
         | 
| 
         @@ -179,6 +180,7 @@ class Model[ 
     | 
|
| 
       179 
180 
     | 
    
         
             
                    :param features: Features of the model. Filled by the feature presets themselves during registration.
         
     | 
| 
       180 
181 
     | 
    
         
             
                    :param imports: List of import formats that can be used to import the record.
         
     | 
| 
       181 
182 
     | 
    
         
             
                        If not provided, no imports are available.
         
     | 
| 
      
 183 
     | 
    
         
            +
                    :param ui_blueprint_name: Name of the UI blueprint
         
     | 
| 
       182 
184 
     | 
    
         
             
                    """
         
     | 
| 
       183 
185 
     | 
    
         
             
                    self._code = code
         
     | 
| 
       184 
186 
     | 
    
         
             
                    self._name = name
         
     | 
| 
         @@ -203,6 +205,7 @@ class Model[ 
     | 
|
| 
       203 
205 
     | 
    
         
             
                    self._imports = imports or []
         
     | 
| 
       204 
206 
     | 
    
         
             
                    self._model_metadata = model_metadata
         
     | 
| 
       205 
207 
     | 
    
         
             
                    self._features = features
         
     | 
| 
      
 208 
     | 
    
         
            +
                    self._ui_blueprint_name = ui_blueprint_name
         
     | 
| 
       206 
209 
     | 
    
         | 
| 
       207 
210 
     | 
    
         
             
                @property
         
     | 
| 
       208 
211 
     | 
    
         
             
                def code(self) -> str:
         
     | 
| 
         @@ -302,6 +305,11 @@ class Model[ 
     | 
|
| 
       302 
305 
     | 
    
         
             
                    """Get the API blueprint name for the model."""
         
     | 
| 
       303 
306 
     | 
    
         
             
                    return cast("str", self.resource_config.blueprint_name)
         
     | 
| 
       304 
307 
     | 
    
         | 
| 
      
 308 
     | 
    
         
            +
                @property
         
     | 
| 
      
 309 
     | 
    
         
            +
                def ui_blueprint_name(self) -> str | None:
         
     | 
| 
      
 310 
     | 
    
         
            +
                    """Get the API blueprint name for the model."""
         
     | 
| 
      
 311 
     | 
    
         
            +
                    return self._ui_blueprint_name
         
     | 
| 
      
 312 
     | 
    
         
            +
             
     | 
| 
       305 
313 
     | 
    
         
             
                @property
         
     | 
| 
       306 
314 
     | 
    
         
             
                def record_pid_type(self) -> str | None:
         
     | 
| 
       307 
315 
     | 
    
         
             
                    """Get the PID type for the model."""
         
     | 
| 
         @@ -342,6 +350,12 @@ class Model[ 
     | 
|
| 
       342 
350 
     | 
    
         
             
                    """Get the API URL for the model."""
         
     | 
| 
       343 
351 
     | 
    
         
             
                    return cast("str", invenio_url_for(f"{self.api_blueprint_name}.{view_name}", **kwargs))
         
     | 
| 
       344 
352 
     | 
    
         | 
| 
      
 353 
     | 
    
         
            +
                def ui_url(self, view_name: str, **kwargs: Any) -> str | None:
         
     | 
| 
      
 354 
     | 
    
         
            +
                    """Get the UI URL for the model."""
         
     | 
| 
      
 355 
     | 
    
         
            +
                    if self.ui_blueprint_name is None:
         
     | 
| 
      
 356 
     | 
    
         
            +
                        return None
         
     | 
| 
      
 357 
     | 
    
         
            +
                    return cast("str", invenio_url_for(f"{self.ui_blueprint_name}.{view_name}", **kwargs))
         
     | 
| 
      
 358 
     | 
    
         
            +
             
     | 
| 
       345 
359 
     | 
    
         
             
                @cached_property
         
     | 
| 
       346 
360 
     | 
    
         
             
                def resource_config(self) -> RC:
         
     | 
| 
       347 
361 
     | 
    
         
             
                    """Get the resource configuration."""
         
     | 
| 
         @@ -374,6 +388,13 @@ class Model[ 
     | 
|
| 
       374 
388 
     | 
    
         
             
                    """Get all exportable response handlers."""
         
     | 
| 
       375 
389 
     | 
    
         
             
                    return self._exports
         
     | 
| 
       376 
390 
     | 
    
         | 
| 
      
 391 
     | 
    
         
            +
                def get_export_by_mimetype(self, mimetype: str) -> Export | None:
         
     | 
| 
      
 392 
     | 
    
         
            +
                    """Get an export by mimetype."""
         
     | 
| 
      
 393 
     | 
    
         
            +
                    for export in self._exports:
         
     | 
| 
      
 394 
     | 
    
         
            +
                        if export.mimetype == mimetype:
         
     | 
| 
      
 395 
     | 
    
         
            +
                            return export
         
     | 
| 
      
 396 
     | 
    
         
            +
                    return None
         
     | 
| 
      
 397 
     | 
    
         
            +
             
     | 
| 
       377 
398 
     | 
    
         
             
                @property
         
     | 
| 
       378 
399 
     | 
    
         
             
                def response_handlers(self) -> dict[str, ResponseHandler]:
         
     | 
| 
       379 
400 
     | 
    
         
             
                    """Get all response handlers from the resource configuration."""
         
     | 
| 
         @@ -388,3 +409,10 @@ class Model[ 
     | 
|
| 
       388 
409 
     | 
    
         
             
                def imports(self) -> list[Import]:
         
     | 
| 
       389 
410 
     | 
    
         
             
                    """Get all importable request body parsers."""
         
     | 
| 
       390 
411 
     | 
    
         
             
                    return self._imports
         
     | 
| 
      
 412 
     | 
    
         
            +
             
     | 
| 
      
 413 
     | 
    
         
            +
                @property
         
     | 
| 
      
 414 
     | 
    
         
            +
                def entity_type(self) -> str:
         
     | 
| 
      
 415 
     | 
    
         
            +
                    """Get the entity type."""
         
     | 
| 
      
 416 
     | 
    
         
            +
                    if self.records_alias_enabled:
         
     | 
| 
      
 417 
     | 
    
         
            +
                        return cast("str", self.service.id)
         
     | 
| 
      
 418 
     | 
    
         
            +
                    raise TypeError("This model does not have associated entity type.")
         
     | 
    
        oarepo_runtime/ext.py
    CHANGED
    
    | 
         @@ -177,6 +177,12 @@ class OARepoRuntime: 
     | 
|
| 
       177 
177 
     | 
    
         
             
                        raise ValueError("Need to pass a record instance, got None")
         
     | 
| 
       178 
178 
     | 
    
         
             
                    return self.get_record_service_for_record_class(type(record))
         
     | 
| 
       179 
179 
     | 
    
         | 
| 
      
 180 
     | 
    
         
            +
                def get_model_for_record(self, record: Any) -> Model:
         
     | 
| 
      
 181 
     | 
    
         
            +
                    """Retrieve the associated service for a given record."""
         
     | 
| 
      
 182 
     | 
    
         
            +
                    if record is None:
         
     | 
| 
      
 183 
     | 
    
         
            +
                        raise ValueError("Need to pass a record instance, got None")
         
     | 
| 
      
 184 
     | 
    
         
            +
                    return self.get_model_for_record_class(type(record))
         
     | 
| 
      
 185 
     | 
    
         
            +
             
     | 
| 
       180 
186 
     | 
    
         
             
                def get_file_service_for_record(self, record: Any) -> FileService | None:
         
     | 
| 
       181 
187 
     | 
    
         
             
                    """Return the file service for the given record (draft or published)."""
         
     | 
| 
       182 
188 
     | 
    
         
             
                    model = self.models_by_record_class.get(type(record))
         
     | 
| 
         @@ -188,13 +194,16 @@ class OARepoRuntime: 
     | 
|
| 
       188 
194 
     | 
    
         
             
                    return model.file_service
         
     | 
| 
       189 
195 
     | 
    
         | 
| 
       190 
196 
     | 
    
         
             
                def get_record_service_for_record_class(self, record_cls: type[RecordBase]) -> RecordService:
         
     | 
| 
      
 197 
     | 
    
         
            +
                    """Retrieve the service associated with a given record class."""
         
     | 
| 
      
 198 
     | 
    
         
            +
                    return self.get_model_for_record_class(record_cls).service
         
     | 
| 
      
 199 
     | 
    
         
            +
             
     | 
| 
      
 200 
     | 
    
         
            +
                def get_model_for_record_class(self, record_cls: type[RecordBase]) -> Model:
         
     | 
| 
       191 
201 
     | 
    
         
             
                    """Retrieve the service associated with a given record class."""
         
     | 
| 
       192 
202 
     | 
    
         
             
                    for t in record_cls.mro():
         
     | 
| 
       193 
203 
     | 
    
         
             
                        if t is RecordBase:
         
     | 
| 
       194 
204 
     | 
    
         
             
                            break
         
     | 
| 
       195 
205 
     | 
    
         
             
                        if t in self.models_by_record_class:
         
     | 
| 
       196 
     | 
    
         
            -
                             
     | 
| 
       197 
     | 
    
         
            -
                            return model.service
         
     | 
| 
      
 206 
     | 
    
         
            +
                            return self.models_by_record_class[t]
         
     | 
| 
       198 
207 
     | 
    
         
             
                    raise KeyError(f"No service found for record class '{record_cls.__name__}'.")
         
     | 
| 
       199 
208 
     | 
    
         | 
| 
       200 
209 
     | 
    
         
             
                @cached_property
         
     | 
| 
         @@ -0,0 +1,365 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            #
         
     | 
| 
      
 2 
     | 
    
         
            +
            # Copyright (c) 2025 CESNET z.s.p.o.
         
     | 
| 
      
 3 
     | 
    
         
            +
            #
         
     | 
| 
      
 4 
     | 
    
         
            +
            # This file is a part of oarepo-runtime (see http://github.com/oarepo/oarepo-runtime).
         
     | 
| 
      
 5 
     | 
    
         
            +
            #
         
     | 
| 
      
 6 
     | 
    
         
            +
            # oarepo-runtime is free software; you can redistribute it and/or modify it
         
     | 
| 
      
 7 
     | 
    
         
            +
            # under the terms of the MIT License; see LICENSE file for more details.
         
     | 
| 
      
 8 
     | 
    
         
            +
            #
         
     | 
| 
      
 9 
     | 
    
         
            +
            """Signposting functionality.
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
            Functions to create a list of signpost links record's landing page, export formats and file contents.
         
     | 
| 
      
 12 
     | 
    
         
            +
            Separate functions to create a complete linkset for the record item in application/linkset or application/linkset+json format.
         
     | 
| 
      
 13 
     | 
    
         
            +
            Function to format the linkset into a HTTP Link header.
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
            Information about relation types can be found at: https://signposting.org/FAIR/#reltypes
         
     | 
| 
      
 16 
     | 
    
         
            +
            Excerpt with explanations about relation types:
         
     | 
| 
      
 17 
     | 
    
         
            +
            author 	        = The target of the link is a URI for an author of the resource that is the origin of the link.
         
     | 
| 
      
 18 
     | 
    
         
            +
            cite-as 	    = The target of the link is a persistent URI for the resource that is the origin of the link. When accessing the persistent URI, it redirects to that origin resource.
         
     | 
| 
      
 19 
     | 
    
         
            +
            describedby 	= The target of the link provides metadata that describes the resource that is the origin of the link. It is the inverse of the describes relation type.
         
     | 
| 
      
 20 
     | 
    
         
            +
            describes 	    = The origin of the link is a resource that provides metadata that describes the resource that is the target of the link. It is the inverse of the describedby relation type.
         
     | 
| 
      
 21 
     | 
    
         
            +
            type 	        = The target of the link is the URI for a class of resources to which the resource that is the origin of the link belongs.
         
     | 
| 
      
 22 
     | 
    
         
            +
            license 	    = The target of the link is the URI of a license that applies to the resource that is the origin of the link.
         
     | 
| 
      
 23 
     | 
    
         
            +
            item 	        = The origin of the link is a collection of resources and the target of the link is a resource that belongs to that collection. It is the inverse of the collection relation type.
         
     | 
| 
      
 24 
     | 
    
         
            +
            collection 	    = The origin of the link is a resource that belongs to a collection and the target of the link is the collection to which it belongs. It is the inverse of the item relation type.
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
            item <-> collection
         
     | 
| 
      
 27 
     | 
    
         
            +
            describedby <-> describes
         
     | 
| 
      
 28 
     | 
    
         
            +
            """  # noqa: E501
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
            from __future__ import annotations
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
            from collections import defaultdict
         
     | 
| 
      
 33 
     | 
    
         
            +
            from typing import TYPE_CHECKING, Any, Literal, cast, overload
         
     | 
| 
      
 34 
     | 
    
         
            +
            from urllib.parse import urljoin
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
            if TYPE_CHECKING:
         
     | 
| 
      
 37 
     | 
    
         
            +
                from invenio_records_resources.services.records.results import RecordItem
         
     | 
| 
      
 38 
     | 
    
         
            +
            from signposting import AbsoluteURI, LinkRel, Signpost
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
            from oarepo_runtime.proxies import current_runtime
         
     | 
| 
      
 41 
     | 
    
         
            +
            from oarepo_runtime.typing import record_from_result
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
            def signpost_link_to_str(signpost_link: Signpost) -> str:
         
     | 
| 
      
 45 
     | 
    
         
            +
                """Convert a signpost link to string."""
         
     | 
| 
      
 46 
     | 
    
         
            +
                link_str = str(signpost_link)
         
     | 
| 
      
 47 
     | 
    
         
            +
                if link_str[:6] == "Link: ":
         
     | 
| 
      
 48 
     | 
    
         
            +
                    return f"{link_str[6:]}"
         
     | 
| 
      
 49 
     | 
    
         
            +
                raise ValueError(f"Invalid signpost link: {link_str}")  # pragma: no cover
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
            def signpost_link_to_dict(link: Signpost) -> dict[str, Any]:
         
     | 
| 
      
 53 
     | 
    
         
            +
                """Convert signpost link to a dictionary."""
         
     | 
| 
      
 54 
     | 
    
         
            +
                link_dict: dict[str, Any] = {"href": link.target}
         
     | 
| 
      
 55 
     | 
    
         
            +
                if link.type:
         
     | 
| 
      
 56 
     | 
    
         
            +
                    link_dict["type"] = link.type
         
     | 
| 
      
 57 
     | 
    
         
            +
                return link_dict
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
            def signpost_link_to_additional_link(
         
     | 
| 
      
 61 
     | 
    
         
            +
                link: Signpost, landing_page_url: str, as_dict: bool = True
         
     | 
| 
      
 62 
     | 
    
         
            +
            ) -> Signpost | dict[str, Any] | None:
         
     | 
| 
      
 63 
     | 
    
         
            +
                """Transform signpost link to additional link with inversed relation type..
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                Args:
         
     | 
| 
      
 66 
     | 
    
         
            +
                    link: A signpost link to transform
         
     | 
| 
      
 67 
     | 
    
         
            +
                    landing_page_url: landing page url which will be passed to href
         
     | 
| 
      
 68 
     | 
    
         
            +
                    as_dict: if true, return dict, else return Signpost
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
                Returns: Additional link as dict or Signpost or None if relation type does no thave additional link.
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                """
         
     | 
| 
      
 73 
     | 
    
         
            +
                match link.rel:
         
     | 
| 
      
 74 
     | 
    
         
            +
                    case LinkRel.item:
         
     | 
| 
      
 75 
     | 
    
         
            +
                        if as_dict:
         
     | 
| 
      
 76 
     | 
    
         
            +
                            return {
         
     | 
| 
      
 77 
     | 
    
         
            +
                                "anchor": link.target,
         
     | 
| 
      
 78 
     | 
    
         
            +
                                str(LinkRel.collection): [{"href": landing_page_url, "type": "text/html"}],
         
     | 
| 
      
 79 
     | 
    
         
            +
                            }
         
     | 
| 
      
 80 
     | 
    
         
            +
                        return Signpost(
         
     | 
| 
      
 81 
     | 
    
         
            +
                            rel=LinkRel.collection, target=landing_page_url, media_type="text/html", context=link.target
         
     | 
| 
      
 82 
     | 
    
         
            +
                        )
         
     | 
| 
      
 83 
     | 
    
         
            +
                    case LinkRel.describedby:
         
     | 
| 
      
 84 
     | 
    
         
            +
                        if as_dict:
         
     | 
| 
      
 85 
     | 
    
         
            +
                            return {
         
     | 
| 
      
 86 
     | 
    
         
            +
                                "anchor": link.target,
         
     | 
| 
      
 87 
     | 
    
         
            +
                                str(LinkRel.describes): [{"href": landing_page_url, "type": "text/html"}],
         
     | 
| 
      
 88 
     | 
    
         
            +
                            }
         
     | 
| 
      
 89 
     | 
    
         
            +
                        return Signpost(rel=LinkRel.describes, target=landing_page_url, media_type="text/html", context=link.target)
         
     | 
| 
      
 90 
     | 
    
         
            +
                    case LinkRel.cite_as:
         
     | 
| 
      
 91 
     | 
    
         
            +
                        return None
         
     | 
| 
      
 92 
     | 
    
         
            +
                    # anchor is generated only for item & describedby, not for license
         
     | 
| 
      
 93 
     | 
    
         
            +
                    case _:
         
     | 
| 
      
 94 
     | 
    
         
            +
                        return None
         
     | 
| 
      
 95 
     | 
    
         
            +
             
     | 
| 
      
 96 
     | 
    
         
            +
             
     | 
| 
      
 97 
     | 
    
         
            +
            def anchor_signpost_link(signpost_link: Signpost, anchor_url: str) -> Signpost:
         
     | 
| 
      
 98 
     | 
    
         
            +
                """Add anchor to a signpost link."""
         
     | 
| 
      
 99 
     | 
    
         
            +
                signpost_link.context = AbsoluteURI(anchor_url)
         
     | 
| 
      
 100 
     | 
    
         
            +
                return signpost_link
         
     | 
| 
      
 101 
     | 
    
         
            +
             
     | 
| 
      
 102 
     | 
    
         
            +
             
     | 
| 
      
 103 
     | 
    
         
            +
            @overload
         
     | 
| 
      
 104 
     | 
    
         
            +
            def get_additional_links(
         
     | 
| 
      
 105 
     | 
    
         
            +
                list_of_signpost_links: list[Signpost], landing_page_url: str, as_dict: Literal[True] = True
         
     | 
| 
      
 106 
     | 
    
         
            +
            ) -> list[dict[str, Any]]: ...
         
     | 
| 
      
 107 
     | 
    
         
            +
             
     | 
| 
      
 108 
     | 
    
         
            +
             
     | 
| 
      
 109 
     | 
    
         
            +
            @overload
         
     | 
| 
      
 110 
     | 
    
         
            +
            def get_additional_links(
         
     | 
| 
      
 111 
     | 
    
         
            +
                list_of_signpost_links: list[Signpost], landing_page_url: str, as_dict: Literal[False]
         
     | 
| 
      
 112 
     | 
    
         
            +
            ) -> list[Signpost]: ...
         
     | 
| 
      
 113 
     | 
    
         
            +
             
     | 
| 
      
 114 
     | 
    
         
            +
             
     | 
| 
      
 115 
     | 
    
         
            +
            def get_additional_links(
         
     | 
| 
      
 116 
     | 
    
         
            +
                list_of_signpost_links: list[Signpost], landing_page_url: str, as_dict: bool = True
         
     | 
| 
      
 117 
     | 
    
         
            +
            ) -> list[Signpost] | list[dict[str, Any]]:
         
     | 
| 
      
 118 
     | 
    
         
            +
                """Create a list of additional links from a list of signpost links.
         
     | 
| 
      
 119 
     | 
    
         
            +
             
     | 
| 
      
 120 
     | 
    
         
            +
                Args:
         
     | 
| 
      
 121 
     | 
    
         
            +
                    list_of_signpost_links: list of signpost link objects to be formatted
         
     | 
| 
      
 122 
     | 
    
         
            +
                    landing_page_url: landing page url
         
     | 
| 
      
 123 
     | 
    
         
            +
                    as_dict: if true, return a list of signpost link dicts, else return a list of Signpost link objects
         
     | 
| 
      
 124 
     | 
    
         
            +
                Returns: list of signpost link dicts or list of Signpost link objects
         
     | 
| 
      
 125 
     | 
    
         
            +
             
     | 
| 
      
 126 
     | 
    
         
            +
                """
         
     | 
| 
      
 127 
     | 
    
         
            +
                results = [
         
     | 
| 
      
 128 
     | 
    
         
            +
                    result
         
     | 
| 
      
 129 
     | 
    
         
            +
                    for signpost_link in list_of_signpost_links
         
     | 
| 
      
 130 
     | 
    
         
            +
                    if (
         
     | 
| 
      
 131 
     | 
    
         
            +
                        result := signpost_link_to_additional_link(
         
     | 
| 
      
 132 
     | 
    
         
            +
                            link=signpost_link, landing_page_url=landing_page_url, as_dict=as_dict
         
     | 
| 
      
 133 
     | 
    
         
            +
                        )
         
     | 
| 
      
 134 
     | 
    
         
            +
                    )
         
     | 
| 
      
 135 
     | 
    
         
            +
                    is not None
         
     | 
| 
      
 136 
     | 
    
         
            +
                ]
         
     | 
| 
      
 137 
     | 
    
         
            +
                if as_dict:
         
     | 
| 
      
 138 
     | 
    
         
            +
                    return cast("list[dict[str, Any]]", results)
         
     | 
| 
      
 139 
     | 
    
         
            +
                return cast("list[Signpost]", results)
         
     | 
| 
      
 140 
     | 
    
         
            +
             
     | 
| 
      
 141 
     | 
    
         
            +
             
     | 
| 
      
 142 
     | 
    
         
            +
            def list_of_signpost_links_to_http_header(links_list: list[Signpost]) -> str:
         
     | 
| 
      
 143 
     | 
    
         
            +
                """Create an HTTP Link header from a list of signpost links.
         
     | 
| 
      
 144 
     | 
    
         
            +
             
     | 
| 
      
 145 
     | 
    
         
            +
                Args:
         
     | 
| 
      
 146 
     | 
    
         
            +
                    links_list: list of signpost link objects to be formatted
         
     | 
| 
      
 147 
     | 
    
         
            +
             
     | 
| 
      
 148 
     | 
    
         
            +
                Returns: signpost header with formatted links.
         
     | 
| 
      
 149 
     | 
    
         
            +
             
     | 
| 
      
 150 
     | 
    
         
            +
                """
         
     | 
| 
      
 151 
     | 
    
         
            +
                links = [str(link)[6:] for link in links_list if str(link)[:6] == "Link: "]
         
     | 
| 
      
 152 
     | 
    
         
            +
                return f"Link: {', '.join(links)}"
         
     | 
| 
      
 153 
     | 
    
         
            +
             
     | 
| 
      
 154 
     | 
    
         
            +
             
     | 
| 
      
 155 
     | 
    
         
            +
            def create_linkset(datacite_dict: dict, record_item: RecordItem) -> str:
         
     | 
| 
      
 156 
     | 
    
         
            +
                """Create a linkset for the record item in the application/linkset format.
         
     | 
| 
      
 157 
     | 
    
         
            +
             
     | 
| 
      
 158 
     | 
    
         
            +
                Args:
         
     | 
| 
      
 159 
     | 
    
         
            +
                    datacite_dict:  dictionary with datacite data
         
     | 
| 
      
 160 
     | 
    
         
            +
                    record_item: record item, for which signpost links should be generated
         
     | 
| 
      
 161 
     | 
    
         
            +
             
     | 
| 
      
 162 
     | 
    
         
            +
                Returns: linkset in string format
         
     | 
| 
      
 163 
     | 
    
         
            +
             
     | 
| 
      
 164 
     | 
    
         
            +
                """
         
     | 
| 
      
 165 
     | 
    
         
            +
                landing_page_url = record_item.links.get("self_html")
         
     | 
| 
      
 166 
     | 
    
         
            +
                # just sanity check, we don't expect this to happen, not covered in tests
         
     | 
| 
      
 167 
     | 
    
         
            +
                if not landing_page_url:  # pragma: no cover
         
     | 
| 
      
 168 
     | 
    
         
            +
                    return ""
         
     | 
| 
      
 169 
     | 
    
         
            +
                landing_page_links = landing_page_signpost_links_list(datacite_dict, record_item, short=False)
         
     | 
| 
      
 170 
     | 
    
         
            +
                additional_links: list[Signpost] = get_additional_links(landing_page_links, landing_page_url, as_dict=False)
         
     | 
| 
      
 171 
     | 
    
         
            +
                anchored_links = [
         
     | 
| 
      
 172 
     | 
    
         
            +
                    anchor_signpost_link(signpost_link, landing_page_url) for signpost_link in landing_page_links
         
     | 
| 
      
 173 
     | 
    
         
            +
                ] + additional_links
         
     | 
| 
      
 174 
     | 
    
         
            +
                links = [str(link)[6:] for link in anchored_links if str(link)[:6] == "Link: "]
         
     | 
| 
      
 175 
     | 
    
         
            +
                return ", ".join(links)
         
     | 
| 
      
 176 
     | 
    
         
            +
             
     | 
| 
      
 177 
     | 
    
         
            +
             
     | 
| 
      
 178 
     | 
    
         
            +
            def create_linkset_json(datacite_dict: dict, record_item: RecordItem) -> dict[str, list[dict[str, Any]]]:
         
     | 
| 
      
 179 
     | 
    
         
            +
                """Create a linkset for the record item in the application/linkset+json format.
         
     | 
| 
      
 180 
     | 
    
         
            +
             
     | 
| 
      
 181 
     | 
    
         
            +
                Args:
         
     | 
| 
      
 182 
     | 
    
         
            +
                    datacite_dict:  dictionary with datacite data
         
     | 
| 
      
 183 
     | 
    
         
            +
                    record_item: record item, for which signpost links should be generated
         
     | 
| 
      
 184 
     | 
    
         
            +
             
     | 
| 
      
 185 
     | 
    
         
            +
                Returns: linkset in JSON format
         
     | 
| 
      
 186 
     | 
    
         
            +
             
     | 
| 
      
 187 
     | 
    
         
            +
                """
         
     | 
| 
      
 188 
     | 
    
         
            +
                landing_page_url = record_item.links.get("self_html")
         
     | 
| 
      
 189 
     | 
    
         
            +
                # just sanity check, we don't expect this to happen, not covered in tests
         
     | 
| 
      
 190 
     | 
    
         
            +
                if not landing_page_url:  # pragma: no cover
         
     | 
| 
      
 191 
     | 
    
         
            +
                    return {}
         
     | 
| 
      
 192 
     | 
    
         
            +
                landing_page_links = landing_page_signpost_links_list(datacite_dict, record_item, short=False)
         
     | 
| 
      
 193 
     | 
    
         
            +
                dict_of_links_by_relation = defaultdict(list)
         
     | 
| 
      
 194 
     | 
    
         
            +
                for link in landing_page_links:
         
     | 
| 
      
 195 
     | 
    
         
            +
                    dict_of_links_by_relation[str(link.rel)].append(link)
         
     | 
| 
      
 196 
     | 
    
         
            +
                links_json = defaultdict(list)
         
     | 
| 
      
 197 
     | 
    
         
            +
                links_json["anchor"] = landing_page_url
         
     | 
| 
      
 198 
     | 
    
         
            +
             
     | 
| 
      
 199 
     | 
    
         
            +
                additional_links: list[dict[str, Any]] = get_additional_links(landing_page_links, landing_page_url)
         
     | 
| 
      
 200 
     | 
    
         
            +
                for link_relation_from_dict, list_of_links_for_relation in dict_of_links_by_relation.items():
         
     | 
| 
      
 201 
     | 
    
         
            +
                    for link in list_of_links_for_relation:
         
     | 
| 
      
 202 
     | 
    
         
            +
                        links_json[link_relation_from_dict].append(signpost_link_to_dict(link))
         
     | 
| 
      
 203 
     | 
    
         
            +
             
     | 
| 
      
 204 
     | 
    
         
            +
                return {"linkset": [dict(links_json), *[x for x in additional_links if x]]}
         
     | 
| 
      
 205 
     | 
    
         
            +
             
     | 
| 
      
 206 
     | 
    
         
            +
             
     | 
| 
      
 207 
     | 
    
         
            +
            def file_content_signpost_links_list(record_item: RecordItem) -> list[Signpost]:
         
     | 
| 
      
 208 
     | 
    
         
            +
                """Create a list of signpost links for the file content of the record item.
         
     | 
| 
      
 209 
     | 
    
         
            +
             
     | 
| 
      
 210 
     | 
    
         
            +
                Args:
         
     | 
| 
      
 211 
     | 
    
         
            +
                    record_item: record item with the file to generate a signpost link for
         
     | 
| 
      
 212 
     | 
    
         
            +
             
     | 
| 
      
 213 
     | 
    
         
            +
                Returns: list with the signpost link for the file content
         
     | 
| 
      
 214 
     | 
    
         
            +
             
     | 
| 
      
 215 
     | 
    
         
            +
                """
         
     | 
| 
      
 216 
     | 
    
         
            +
                record = record_from_result(record_item)
         
     | 
| 
      
 217 
     | 
    
         
            +
                model = current_runtime.get_model_for_record(record)
         
     | 
| 
      
 218 
     | 
    
         
            +
                landing_page_url = model.ui_url(view_name="record_detail", pid_value=record.pid.pid_value)
         
     | 
| 
      
 219 
     | 
    
         
            +
                if not landing_page_url:  # pragma: no cover
         
     | 
| 
      
 220 
     | 
    
         
            +
                    return []
         
     | 
| 
      
 221 
     | 
    
         
            +
                return [
         
     | 
| 
      
 222 
     | 
    
         
            +
                    Signpost(
         
     | 
| 
      
 223 
     | 
    
         
            +
                        rel=LinkRel.linkset,
         
     | 
| 
      
 224 
     | 
    
         
            +
                        target=landing_page_url,
         
     | 
| 
      
 225 
     | 
    
         
            +
                        media_type="application/linkset",
         
     | 
| 
      
 226 
     | 
    
         
            +
                    ),
         
     | 
| 
      
 227 
     | 
    
         
            +
                    Signpost(
         
     | 
| 
      
 228 
     | 
    
         
            +
                        rel=LinkRel.linkset,
         
     | 
| 
      
 229 
     | 
    
         
            +
                        target=landing_page_url,
         
     | 
| 
      
 230 
     | 
    
         
            +
                        media_type="application/linkset+json",
         
     | 
| 
      
 231 
     | 
    
         
            +
                    ),
         
     | 
| 
      
 232 
     | 
    
         
            +
                    Signpost(
         
     | 
| 
      
 233 
     | 
    
         
            +
                        rel=LinkRel.collection,
         
     | 
| 
      
 234 
     | 
    
         
            +
                        target=landing_page_url,
         
     | 
| 
      
 235 
     | 
    
         
            +
                        media_type="text/html",
         
     | 
| 
      
 236 
     | 
    
         
            +
                    ),
         
     | 
| 
      
 237 
     | 
    
         
            +
                ]
         
     | 
| 
      
 238 
     | 
    
         
            +
             
     | 
| 
      
 239 
     | 
    
         
            +
             
     | 
| 
      
 240 
     | 
    
         
            +
            def export_format_signpost_links_list(record_item: RecordItem) -> list[Signpost]:
         
     | 
| 
      
 241 
     | 
    
         
            +
                """Create a list of signpost links for the export format of the record item.
         
     | 
| 
      
 242 
     | 
    
         
            +
             
     | 
| 
      
 243 
     | 
    
         
            +
                Args:
         
     | 
| 
      
 244 
     | 
    
         
            +
                record_item: record item with the export format to generate a signpost link for
         
     | 
| 
      
 245 
     | 
    
         
            +
                code: code of the export format
         
     | 
| 
      
 246 
     | 
    
         
            +
             
     | 
| 
      
 247 
     | 
    
         
            +
                Returns: list with the signpost link for the export format
         
     | 
| 
      
 248 
     | 
    
         
            +
             
     | 
| 
      
 249 
     | 
    
         
            +
                """
         
     | 
| 
      
 250 
     | 
    
         
            +
                return [
         
     | 
| 
      
 251 
     | 
    
         
            +
                    Signpost(
         
     | 
| 
      
 252 
     | 
    
         
            +
                        rel=LinkRel.linkset,
         
     | 
| 
      
 253 
     | 
    
         
            +
                        target=record_item.links["self_html"],
         
     | 
| 
      
 254 
     | 
    
         
            +
                        media_type="application/linkset",
         
     | 
| 
      
 255 
     | 
    
         
            +
                    ),
         
     | 
| 
      
 256 
     | 
    
         
            +
                    Signpost(
         
     | 
| 
      
 257 
     | 
    
         
            +
                        rel=LinkRel.linkset,
         
     | 
| 
      
 258 
     | 
    
         
            +
                        target=record_item.links["self_html"],
         
     | 
| 
      
 259 
     | 
    
         
            +
                        media_type="application/linkset+json",
         
     | 
| 
      
 260 
     | 
    
         
            +
                    ),
         
     | 
| 
      
 261 
     | 
    
         
            +
                    Signpost(rel=LinkRel.describes, target=record_item.links["self_html"], media_type="text/html"),
         
     | 
| 
      
 262 
     | 
    
         
            +
                ]
         
     | 
| 
      
 263 
     | 
    
         
            +
             
     | 
| 
      
 264 
     | 
    
         
            +
             
     | 
| 
      
 265 
     | 
    
         
            +
            def landing_page_signpost_links_list(datacite_dict: dict, record_item: RecordItem, short: bool) -> list[Signpost]:
         
     | 
| 
      
 266 
     | 
    
         
            +
                """Create a list of signpost links for the landing page of the record item.
         
     | 
| 
      
 267 
     | 
    
         
            +
             
     | 
| 
      
 268 
     | 
    
         
            +
                Args:
         
     | 
| 
      
 269 
     | 
    
         
            +
                    datacite_dict: dictionary with datacite data
         
     | 
| 
      
 270 
     | 
    
         
            +
                    record_item: record item, for which signpost links should be generated
         
     | 
| 
      
 271 
     | 
    
         
            +
                    short: If true, lists only the first three links for relations with greater count
         
     | 
| 
      
 272 
     | 
    
         
            +
             
     | 
| 
      
 273 
     | 
    
         
            +
                Returns: list of signpost links for the landing page
         
     | 
| 
      
 274 
     | 
    
         
            +
             
     | 
| 
      
 275 
     | 
    
         
            +
                """
         
     | 
| 
      
 276 
     | 
    
         
            +
                signposting_links: list[Signpost] = []
         
     | 
| 
      
 277 
     | 
    
         
            +
                record = record_from_result(record_item)
         
     | 
| 
      
 278 
     | 
    
         
            +
                record_data = record_item.data  # self.html
         
     | 
| 
      
 279 
     | 
    
         
            +
                record_files = record_data.get("files", {}).get("entries", {})
         
     | 
| 
      
 280 
     | 
    
         
            +
                model = current_runtime.get_model_for_record(record)
         
     | 
| 
      
 281 
     | 
    
         
            +
             
     | 
| 
      
 282 
     | 
    
         
            +
                # author - prvni tri
         
     | 
| 
      
 283 
     | 
    
         
            +
                data = datacite_dict["data"]
         
     | 
| 
      
 284 
     | 
    
         
            +
                attributes = data["attributes"]
         
     | 
| 
      
 285 
     | 
    
         
            +
                creators = attributes.get("creators", [])
         
     | 
| 
      
 286 
     | 
    
         
            +
                if short:
         
     | 
| 
      
 287 
     | 
    
         
            +
                    creators = creators[:3]
         
     | 
| 
      
 288 
     | 
    
         
            +
                for attribute in creators:
         
     | 
| 
      
 289 
     | 
    
         
            +
                    signposting_links.extend(
         
     | 
| 
      
 290 
     | 
    
         
            +
                        Signpost(rel=LinkRel.author, target=name_identifier["nameIdentifier"])
         
     | 
| 
      
 291 
     | 
    
         
            +
                        for name_identifier in attribute["nameIdentifiers"]
         
     | 
| 
      
 292 
     | 
    
         
            +
                    )
         
     | 
| 
      
 293 
     | 
    
         
            +
             
     | 
| 
      
 294 
     | 
    
         
            +
                # cite-as = DOI
         
     | 
| 
      
 295 
     | 
    
         
            +
                signposting_links.append(Signpost(rel=LinkRel.cite_as, target=urljoin("https://doi.org/", attributes.get("doi"))))
         
     | 
| 
      
 296 
     | 
    
         
            +
             
     | 
| 
      
 297 
     | 
    
         
            +
                # describedby
         
     | 
| 
      
 298 
     | 
    
         
            +
                for model_export in model.exports:
         
     | 
| 
      
 299 
     | 
    
         
            +
                    model_export_url = model.ui_url(
         
     | 
| 
      
 300 
     | 
    
         
            +
                        view_name="export", pid_value=record.pid.pid_value, export_format=model_export.code
         
     | 
| 
      
 301 
     | 
    
         
            +
                    )
         
     | 
| 
      
 302 
     | 
    
         
            +
                    # just sanity check, we don't expect this to happen, not covered in tests
         
     | 
| 
      
 303 
     | 
    
         
            +
                    if not model_export_url:  # pragma: no cover
         
     | 
| 
      
 304 
     | 
    
         
            +
                        continue
         
     | 
| 
      
 305 
     | 
    
         
            +
                    signposting_links.append(
         
     | 
| 
      
 306 
     | 
    
         
            +
                        Signpost(rel=LinkRel.describedby, target=model_export_url, media_type=model_export.mimetype)
         
     | 
| 
      
 307 
     | 
    
         
            +
                    )
         
     | 
| 
      
 308 
     | 
    
         
            +
             
     | 
| 
      
 309 
     | 
    
         
            +
                # item
         
     | 
| 
      
 310 
     | 
    
         
            +
                record_file_values = record_files.values()
         
     | 
| 
      
 311 
     | 
    
         
            +
                if short:
         
     | 
| 
      
 312 
     | 
    
         
            +
                    record_file_values = list(record_file_values)[:3]
         
     | 
| 
      
 313 
     | 
    
         
            +
                record_files_url = record_item.links.get("files")
         
     | 
| 
      
 314 
     | 
    
         
            +
                if record_files_url:
         
     | 
| 
      
 315 
     | 
    
         
            +
                    signposting_links.extend(
         
     | 
| 
      
 316 
     | 
    
         
            +
                        Signpost(
         
     | 
| 
      
 317 
     | 
    
         
            +
                            rel=LinkRel.item,
         
     | 
| 
      
 318 
     | 
    
         
            +
                            media_type=record_file.get("mimetype"),
         
     | 
| 
      
 319 
     | 
    
         
            +
                            target=f"{record_files_url}/{record_file.get('key')}",
         
     | 
| 
      
 320 
     | 
    
         
            +
                        )
         
     | 
| 
      
 321 
     | 
    
         
            +
                        for record_file in record_file_values
         
     | 
| 
      
 322 
     | 
    
         
            +
                    )
         
     | 
| 
      
 323 
     | 
    
         
            +
             
     | 
| 
      
 324 
     | 
    
         
            +
                # license
         
     | 
| 
      
 325 
     | 
    
         
            +
                for attribute in attributes.get("rightsList"):
         
     | 
| 
      
 326 
     | 
    
         
            +
                    # check for schemeUri, rightsIdentifier and 'rightsIdentifierScheme' == SPDX, fallback rightsUri, else nothing
         
     | 
| 
      
 327 
     | 
    
         
            +
                    license_url = attribute.get("rightsUri")
         
     | 
| 
      
 328 
     | 
    
         
            +
                    if (
         
     | 
| 
      
 329 
     | 
    
         
            +
                        attribute.get("schemeUri")
         
     | 
| 
      
 330 
     | 
    
         
            +
                        and attribute.get("rightsIdentifier")
         
     | 
| 
      
 331 
     | 
    
         
            +
                        and attribute.get("rightsIdentifierScheme") == "SPDX"
         
     | 
| 
      
 332 
     | 
    
         
            +
                    ):
         
     | 
| 
      
 333 
     | 
    
         
            +
                        license_url = urljoin(attribute.get("schemeUri"), attribute.get("rightsIdentifier"))
         
     | 
| 
      
 334 
     | 
    
         
            +
                    signposting_links.append(Signpost(rel=LinkRel.license, target=license_url))
         
     | 
| 
      
 335 
     | 
    
         
            +
             
     | 
| 
      
 336 
     | 
    
         
            +
                # type
         
     | 
| 
      
 337 
     | 
    
         
            +
                schema_org = attributes.get("types", {}).get("schemaOrg")
         
     | 
| 
      
 338 
     | 
    
         
            +
                if schema_org:
         
     | 
| 
      
 339 
     | 
    
         
            +
                    resource_type_url = "https://schema.org/" + schema_org
         
     | 
| 
      
 340 
     | 
    
         
            +
                    signposting_links.append(Signpost(rel=LinkRel.type, target=resource_type_url))
         
     | 
| 
      
 341 
     | 
    
         
            +
                signposting_links.append(Signpost(rel=LinkRel.type, target="https://schema.org/AboutPage"))
         
     | 
| 
      
 342 
     | 
    
         
            +
             
     | 
| 
      
 343 
     | 
    
         
            +
                return signposting_links
         
     | 
| 
      
 344 
     | 
    
         
            +
             
     | 
| 
      
 345 
     | 
    
         
            +
             
     | 
| 
      
 346 
     | 
    
         
            +
            def record_to_linkset(record_item: RecordItem) -> str:
         
     | 
| 
      
 347 
     | 
    
         
            +
                """Create a linkset from the record item. Get datacite to build linkset from model exports."""
         
     | 
| 
      
 348 
     | 
    
         
            +
                record = record_from_result(record_item)
         
     | 
| 
      
 349 
     | 
    
         
            +
                model = current_runtime.get_model_for_record(record)
         
     | 
| 
      
 350 
     | 
    
         
            +
                datacite_export = model.get_export_by_mimetype("application/vnd.datacite.datacite+json")
         
     | 
| 
      
 351 
     | 
    
         
            +
                if not datacite_export:
         
     | 
| 
      
 352 
     | 
    
         
            +
                    return ""
         
     | 
| 
      
 353 
     | 
    
         
            +
                datacite_dict = datacite_export.serializer.serialize_object(record)
         
     | 
| 
      
 354 
     | 
    
         
            +
                return create_linkset(datacite_dict, record_item)
         
     | 
| 
      
 355 
     | 
    
         
            +
             
     | 
| 
      
 356 
     | 
    
         
            +
             
     | 
| 
      
 357 
     | 
    
         
            +
            def record_to_json_linkset(record_item: RecordItem) -> dict[str, list[dict[str, Any]]]:
         
     | 
| 
      
 358 
     | 
    
         
            +
                """Create a JSON linkset from the record item. Get datacite to build linkset from model exports."""
         
     | 
| 
      
 359 
     | 
    
         
            +
                record = record_from_result(record_item)
         
     | 
| 
      
 360 
     | 
    
         
            +
                model = current_runtime.get_model_for_record(record)
         
     | 
| 
      
 361 
     | 
    
         
            +
                datacite_export = model.get_export_by_mimetype("application/vnd.datacite.datacite+json")
         
     | 
| 
      
 362 
     | 
    
         
            +
                if not datacite_export:
         
     | 
| 
      
 363 
     | 
    
         
            +
                    return {}
         
     | 
| 
      
 364 
     | 
    
         
            +
                datacite_dict = datacite_export.serializer.serialize_object(record)
         
     | 
| 
      
 365 
     | 
    
         
            +
                return create_linkset_json(datacite_dict, record_item)
         
     | 
| 
         @@ -1,6 +1,6 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            Metadata-Version: 2.4
         
     | 
| 
       2 
2 
     | 
    
         
             
            Name: oarepo-runtime
         
     | 
| 
       3 
     | 
    
         
            -
            Version: 2.0.0. 
     | 
| 
      
 3 
     | 
    
         
            +
            Version: 2.0.0.dev40
         
     | 
| 
       4 
4 
     | 
    
         
             
            Summary: A set of runtime extensions of Invenio repository
         
     | 
| 
       5 
5 
     | 
    
         
             
            Project-URL: Homepage, https://github.com/oarepo/oarepo-runtime
         
     | 
| 
       6 
6 
     | 
    
         
             
            License-Expression: MIT
         
     | 
| 
         @@ -9,6 +9,7 @@ Requires-Python: <3.14,>=3.13 
     | 
|
| 
       9 
9 
     | 
    
         
             
            Requires-Dist: langcodes>=3.5.0
         
     | 
| 
       10 
10 
     | 
    
         
             
            Requires-Dist: oarepo-invenio-typing-stubs>=0.1.0
         
     | 
| 
       11 
11 
     | 
    
         
             
            Requires-Dist: oarepo[rdm,tests]<15,>=14
         
     | 
| 
      
 12 
     | 
    
         
            +
            Requires-Dist: signposting>=0.9.9
         
     | 
| 
       12 
13 
     | 
    
         
             
            Provides-Extra: dev
         
     | 
| 
       13 
14 
     | 
    
         
             
            Requires-Dist: pytest>=7.1.2; extra == 'dev'
         
     | 
| 
       14 
15 
     | 
    
         
             
            Provides-Extra: oarepo14
         
     | 
| 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            oarepo_runtime/__init__.py,sha256= 
     | 
| 
       2 
     | 
    
         
            -
            oarepo_runtime/api.py,sha256= 
     | 
| 
      
 1 
     | 
    
         
            +
            oarepo_runtime/__init__.py,sha256=ovgPvyXZ4RbBBG9BuRiLv_4Q1TSPMYzOP1vE2STwWT8,686
         
     | 
| 
      
 2 
     | 
    
         
            +
            oarepo_runtime/api.py,sha256=CXprwgOPA3lA69GITWx1xt0AvEuqz2WgdzUYftaHOxo,15247
         
     | 
| 
       3 
3 
     | 
    
         
             
            oarepo_runtime/config.py,sha256=RUEPFn_5bKp9Wb0OY-Fb3VK30m35vF5IsLjYaQHhP3g,3838
         
     | 
| 
       4 
     | 
    
         
            -
            oarepo_runtime/ext.py,sha256= 
     | 
| 
      
 4 
     | 
    
         
            +
            oarepo_runtime/ext.py,sha256=OdhT4gGKK2LpNcJ30cHndCxWZgeHdAk1Ev8bISeX8Qk,9321
         
     | 
| 
       5 
5 
     | 
    
         
             
            oarepo_runtime/proxies.py,sha256=x8Y1iTP8QIzSI67s90VR0_5fvXuT1xlJXtAHsaoXFwg,903
         
     | 
| 
       6 
6 
     | 
    
         
             
            oarepo_runtime/py.typed,sha256=RznSCjXReEUI9zkmD25E8XniG_MvPpLBF6MyNZA8MmE,42
         
     | 
| 
       7 
7 
     | 
    
         
             
            oarepo_runtime/typing.py,sha256=VtINHm4BZ5OJ4KdRAwnFXPZiAEgPRIYTtPC9fIzK1bU,1876
         
     | 
| 
         @@ -22,6 +22,7 @@ oarepo_runtime/records/systemfields/relations.py,sha256=EyFTpdglkRCeCtNg3lXTh3H2 
     | 
|
| 
       22 
22 
     | 
    
         
             
            oarepo_runtime/records/systemfields/selectors.py,sha256=ijVDwAXaXTV5NtcXsrALkhddgCogLNe2eEscFr23qyg,1656
         
     | 
| 
       23 
23 
     | 
    
         
             
            oarepo_runtime/resources/__init__.py,sha256=voynQULXoOEviADkbOpekMphZPTAz4IOTg5BF9xPwTM,453
         
     | 
| 
       24 
24 
     | 
    
         
             
            oarepo_runtime/resources/config.py,sha256=Lbx1QPWAJ8z1truhYntbnhGGWp2OCcwqKm6BuvPJNT0,1330
         
     | 
| 
      
 25 
     | 
    
         
            +
            oarepo_runtime/resources/signposting/__init__.py,sha256=USztM57sVm1LWxACxt6o8cy3VZS0Kz-MYrjG8Jm-Ock,14959
         
     | 
| 
       25 
26 
     | 
    
         
             
            oarepo_runtime/services/__init__.py,sha256=OGtBgEeaDTyk2RPDNXuKbU9_7egFBZr42SM0gN5FrF4,341
         
     | 
| 
       26 
27 
     | 
    
         
             
            oarepo_runtime/services/generators.py,sha256=8Z2QGzob4c2vaaNqhcMZsRybmwtOt30Plgf3EFmcJXw,4622
         
     | 
| 
       27 
28 
     | 
    
         
             
            oarepo_runtime/services/results.py,sha256=EwMW1ed7u6uozgOLZpFa07-NKC89hJlHaVSD8-D5ibU,7211
         
     | 
| 
         @@ -43,8 +44,8 @@ oarepo_runtime/services/schema/__init__.py,sha256=jgAPI_uKC6Ug4KQWnwQVg3-aNaw-eH 
     | 
|
| 
       43 
44 
     | 
    
         
             
            oarepo_runtime/services/schema/i18n.py,sha256=9D1zOQaPKAnYzejB0vO-m2BJYnam0N0Lrq4jID7twfE,3174
         
     | 
| 
       44 
45 
     | 
    
         
             
            oarepo_runtime/services/schema/i18n_ui.py,sha256=DbusphhGDeaobTt4nuwNgKZ6Houlu4Sv3SuMGkdjRRY,3582
         
     | 
| 
       45 
46 
     | 
    
         
             
            oarepo_runtime/services/schema/ui.py,sha256=Y_jBO-fowkpOgceWz8aqJSJAUiAnKLGSIuNpjNLnp8Q,4612
         
     | 
| 
       46 
     | 
    
         
            -
            oarepo_runtime-2.0.0. 
     | 
| 
       47 
     | 
    
         
            -
            oarepo_runtime-2.0.0. 
     | 
| 
       48 
     | 
    
         
            -
            oarepo_runtime-2.0.0. 
     | 
| 
       49 
     | 
    
         
            -
            oarepo_runtime-2.0.0. 
     | 
| 
       50 
     | 
    
         
            -
            oarepo_runtime-2.0.0. 
     | 
| 
      
 47 
     | 
    
         
            +
            oarepo_runtime-2.0.0.dev40.dist-info/METADATA,sha256=sQI5ZRMimfnSlqnHvrzNW7VYDgvxi9spTH8UKxOJ720,4660
         
     | 
| 
      
 48 
     | 
    
         
            +
            oarepo_runtime-2.0.0.dev40.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
         
     | 
| 
      
 49 
     | 
    
         
            +
            oarepo_runtime-2.0.0.dev40.dist-info/entry_points.txt,sha256=rOfs8R1oXFN_dLH9zAZ6ydkvr83mDajegc6NBIRsCMQ,318
         
     | 
| 
      
 50 
     | 
    
         
            +
            oarepo_runtime-2.0.0.dev40.dist-info/licenses/LICENSE,sha256=h2uWz0OaB3EN-J1ImdGJZzc7yvfQjvHVYdUhQ-H7ypY,1064
         
     | 
| 
      
 51 
     | 
    
         
            +
            oarepo_runtime-2.0.0.dev40.dist-info/RECORD,,
         
     | 
| 
         
            File without changes
         
     | 
    
        {oarepo_runtime-2.0.0.dev38.dist-info → oarepo_runtime-2.0.0.dev40.dist-info}/entry_points.txt
    RENAMED
    
    | 
         
            File without changes
         
     | 
    
        {oarepo_runtime-2.0.0.dev38.dist-info → oarepo_runtime-2.0.0.dev40.dist-info}/licenses/LICENSE
    RENAMED
    
    | 
         
            File without changes
         
     |