hermes.commands.deposit.invenio
===============================

.. py:module:: hermes.commands.deposit.invenio


Attributes
----------

.. autoapisummary::

   hermes.commands.deposit.invenio._log


Classes
-------

.. autoapisummary::

   hermes.commands.deposit.invenio.InvenioClient
   hermes.commands.deposit.invenio.InvenioResolver
   hermes.commands.deposit.invenio.InvenioDepositSettings
   hermes.commands.deposit.invenio.InvenioDepositPlugin


Module Contents
---------------

.. py:data:: _log

.. py:class:: InvenioClient(config, auth_token=None, platform_name=None)

   Bases: :py:obj:`requests.Session`


   A Requests session.

   Provides cookie persistence, connection-pooling, and configuration.

   Basic Usage::

     >>> import requests
     >>> s = requests.Session()
     >>> s.get('https://httpbin.org/get')
     <Response [200]>

   Or as a context manager::

     >>> with requests.Session() as s:
     ...     s.get('https://httpbin.org/get')
     <Response [200]>


   .. py:attribute:: DEFAULT_LICENSES_API_PATH
      :value: 'api/licenses'



   .. py:attribute:: DEFAULT_COMMUNITIES_API_PATH
      :value: 'api/communities'



   .. py:attribute:: DEFAULT_DEPOSITIONS_API_PATH
      :value: 'api/deposit/depositions'



   .. py:attribute:: DEFAULT_RECORDS_API_PATH
      :value: 'api/records'



   .. py:attribute:: platform_name
      :value: 'invenio'



   .. py:attribute:: config


   .. py:attribute:: auth_token
      :value: None



   .. py:attribute:: site_url


   .. py:method:: request(method, url, headers=None, **kwargs) -> requests.Response

      Overridden request method to automatically set Authorization header for all requests to the configured site.

      See [requests documentation](https://requests.readthedocs.io/en/latest/api.html#requests.request) for details.



   .. py:method:: get_record(record_id)


   .. py:method:: get_deposit(latest_record_id)


   .. py:method:: get_license(license_id)


   .. py:method:: get_community(community_id)


   .. py:method:: new_deposit()


   .. py:property:: api_paths


   .. py:property:: licenses_api_path


   .. py:property:: communities_api_path


   .. py:property:: depositions_api_path


   .. py:property:: records_api_path


.. py:class:: InvenioResolver(client=None)

   .. py:attribute:: invenio_client_class


   .. py:attribute:: client


   .. py:method:: resolve_latest_id(record_id=None, doi=None, codemeta_identifier=None) -> Tuple[Optional[str], dict]

      Using the given metadata parameters, figure out the latest record id.

      If ``record_id`` is given, it will be used to identify the latest version of the
      record. Otherwise, if there is a DOI present (either as ``doi`` parameter or as
      ``codemeta_identifier``), the DOI will be used to resolve the base record id.

      Either way the record id will be used to resolve the latest version.

      If any of the resolution steps fail or produce an unexpected result, a
      ``ValueError`` will be raised.



   .. py:method:: resolve_doi(doi) -> str

      Resolve a DOI to an Invenio URL and extract the record id.

      :param doi: The DOI to be resolved (only the identifier *without* the ``https://doi.org/`` prefix).
      :return: The record ID on the respective instance.



   .. py:method:: resolve_record_id(record_id: str) -> Tuple[str, dict]

      Find the latest version of a given record.

      :param record_id: The record that sould be resolved.
      :return: The record id of the latest version for the requested record.



   .. py:method:: resolve_license_id(license_url: Optional[str]) -> Optional[str]

      Get Invenio license representation from CodeMeta.

      The license to use is extracted from the ``license`` field in the
      :class:`CodeMetaContext` and converted into an appropiate license identifier to be
      passed to an Invenio instance.

      A license according to CodeMeta may be a URL (text) or a CreativeWork. This function
      only handles URLs. If a ``license`` field is present in the CodeMeta and it is not
      of type :class:`str`, a :class:`RuntimeError` is raised.

      Invenio instances take a license string which refers to a license identifier.
      Typically, Invenio instances offer licenses from https://opendefinition.org and
      https://spdx.org. However, it is possible to mint PIDs for custom licenses.

      An API endpoint (usually ``/api/licenses``) can be used to check whether a given
      license is supported by the Invenio instance. This function tries to retrieve the
      license by the identifier at the end of the license URL path. If this identifier
      does not exist on the Invenio instance, a :class:`RuntimeError` is raised. If no
      license is given in the CodeMeta, ``None`` is returned.



   .. py:method:: _extract_license_id_from_response(data: dict) -> str
      :staticmethod:



.. py:class:: InvenioDepositSettings(/, **data: Any)

   Bases: :py:obj:`pydantic.BaseModel`


   Settings required to deposit into Invenio(RDM).


   .. py:attribute:: site_url
      :type:  str
      :value: ''



   .. py:attribute:: communities
      :type:  list[str]
      :value: None



   .. py:attribute:: access_right
      :type:  str
      :value: None



   .. py:attribute:: embargo_date
      :type:  str
      :value: None



   .. py:attribute:: access_conditions
      :type:  str
      :value: None



   .. py:attribute:: api_paths
      :type:  Dict


   .. py:attribute:: auth_token
      :type:  str
      :value: ''



   .. py:attribute:: files
      :type:  list[pathlib.Path]
      :value: []



   .. py:attribute:: record_id
      :type:  int
      :value: None



   .. py:attribute:: doi
      :type:  str
      :value: None



.. py:class:: InvenioDepositPlugin(command: hermes.commands.deposit.base.HermesDepositCommand, ctx: hermes.model.context.CodeMetaContext, client=None, resolver=None)

   Bases: :py:obj:`hermes.commands.deposit.base.BaseDepositPlugin`


   Base class that implements the generic deposition workflow.

   TODO: describe workflow... needs refactoring to be less stateful!


   .. py:attribute:: platform_name
      :value: 'invenio'



   .. py:attribute:: invenio_client_class


   .. py:attribute:: invenio_resolver_class


   .. py:attribute:: settings_class


   .. py:attribute:: invenio_context_path


   .. py:attribute:: invenio_ctx
      :value: None



   .. py:attribute:: config


   .. py:attribute:: resolver


   .. py:attribute:: links


   .. py:method:: prepare() -> None

      Prepare the deposition on an Invenio-based platform.

      In this function we do the following:

      - resolve the latest published version of this publication (if any)
      - check whether the current version (given in the CodeMeta) was already published
      - check whether we have a valid license identifier (if any)
      - check wether the communities are valid (if configured)
      - check access modalities (access right, access conditions, embargo data, existence
        of license)
      - check whether required configuration options are present
      - update ``self.ctx`` with metadata collected during the checks



   .. py:method:: map_metadata() -> None

      Map the harvested metadata onto the Invenio schema.



   .. py:method:: is_initial_publication() -> bool

      Decide whether to do an initial publication or publish a new version.

      Returning ``True`` indicates that publication of an initial version will be executed, resulting in a call of
      :meth:`create_initial_version`. ``False`` indicates a new version of an existing publication, leading to a call
      of :meth:`create_new_version`.

      By default, this returns ``True``.



   .. py:method:: create_initial_version() -> None

      Create an initial version of a publication.



   .. py:method:: create_new_version() -> None

      Create a new version of an existing publication.



   .. py:method:: related_identifiers()

      Return desired related identifiers.

      In all cases, we add HERMES as ``isCompiledBy`` relation to be able to trace and
      advertise HERMES usage across publication repositories.



   .. py:method:: update_metadata() -> None

      Update the metadata of a draft.



   .. py:method:: delete_artifacts() -> None

      Delete existing file artifacts.

      This is done so that files which existed in an earlier publication but don't exist
      any more, are removed. Otherwise they would cause an error because the didn't change
      between versions.



   .. py:method:: upload_artifacts() -> None

      Upload file artifacts to the deposit.

      We'll use the bucket API rather than the files API as it supports file sizes above
      100MB.



   .. py:method:: publish() -> None

      Publish the deposited record.



   .. py:method:: _codemeta_to_invenio_deposition() -> dict

      The mapping logic.

      Functionality similar to this exists in the ``convert_codemeta`` package which uses
      the crosswalk tables to do the mapping:

      .. code-block:: python

        invenio_metadata = convert_codemeta.crosswalk(
            metadata, "codemeta", "Zenodo"
        )

      Unfortunately, this doesn't work well with additional metadata in the same dict, so
      it is safer to provide our own implementation.

      Currently, this function handles a lot of cases which we want to be able to
      configure. A simple mapping from one JSON path to another is not enough.

      The metadata expected by Zenodo is described in the `Zenodo Developers guide
      <https://developers.zenodo.org/#representation>`_. Unfortunately, there doesn't seem
      to be a schema one can download in order to validate these metadata. There might be
      differences between Invenio-based platforms.



   .. py:method:: _get_license_identifier() -> Optional[str]

      Get Invenio license identifier that matches the given license URL.

      If no license is configured, ``None``  will be returned.



   .. py:method:: _get_community_identifiers()

      Get Invenio community identifiers from config.

      This function gets the communities to be used for the deposition on an Invenio-based
      site from the config and checks their validity against the site's API. If one of the
      identifiers can not be found on the site, a :class:`MisconfigurationError` is
      raised.



   .. py:method:: _get_access_modalities(license)

      Get access right, embargo date and access conditions based on configuration and given license.

      This function implements the rules laid out in the `Zenodo developer documentation
      <https://developers.zenodo.org/#representation>`_:

      - ``access_right`` is a controlled vocabulary
      - embargoed access depositions need an embargo date
      - restricted access depositions need access conditions
      - open and embargoed access depositions need a license
      - closed access depositions have no further requirements

      This function also makes sure that the given embargo date can be parsed as an ISO
      8601 string representation and that the access rights are given as a string.



