# -*- coding: utf-8 -*-
from setuptools import setup

packages = \
['bas_metadata_library',
 'bas_metadata_library.schemas',
 'bas_metadata_library.schemas.dist',
 'bas_metadata_library.schemas.src',
 'bas_metadata_library.standards',
 'bas_metadata_library.standards.iso_19115_1',
 'bas_metadata_library.standards.iso_19115_2',
 'bas_metadata_library.standards.iso_19115_common']

package_data = \
{'': ['*']}

install_requires = \
['backports-datetime-fromisoformat>=1.0.0,<2.0.0',
 'importlib-resources>=5.2.2,<6.0.0',
 'jsonschema==3.2.0',
 'lxml==4.6.3',
 'rfc3987>=1.3.8,<2.0.0',
 'strict-rfc3339>=0.7,<0.8']

setup_kwargs = {
    'name': 'bas-metadata-library',
    'version': '0.5.0rc2',
    'description': 'Python library for generating metadata records',
    'long_description': '# BAS Metadata Library\n\nPython library for generating metadata records.\n\n## Overview\n\n### Purpose\n\nThis library is designed to assist in generating metadata records for the discovery of datasets, services and related\nresources. As a library, this project is intended to be used as a dependency within other tools and services, to\navoid the need to duplicate the implementation of complex and verbose metadata standards.\n\nThis library is built around the needs of the British Antarctic Survey and NERC (UK) Polar Data Centre. This means only\nstandards, and elements of these standards, used by BAS or the UK PDC are supported. However, additions that would\nenable this library to be useful to other organisations and use-case are welcome as contributions providing they do not\nadd significant complexity or maintenance.\n\n### Supported standards\n\n| Standard                                                    | Implementation                                              | Library Namespace                               | Introduced In                                                                                    |\n| ----------------------------------------------------------- | ----------------------------------------------------------- | ----------------------------------------------- | ------------------------------------------------------------------------------------------------ |\n| [ISO 19115:2003](https://www.iso.org/standard/26020.html)   | [ISO 19139:2007](https://www.iso.org/standard/32557.html)   | `bas_metadata_library.standards.iso_19115_1_v1` | [#46](https://gitlab.data.bas.ac.uk/uk-pdc/metadata-infrastructure/metadata-generator/issues/46) |\n| [ISO 19115-2:2009](https://www.iso.org/standard/39229.html) | [ISO 19139-2:2012](https://www.iso.org/standard/57104.html) | `bas_metadata_library.standards.iso_19115_2_v1` | [#50](https://gitlab.data.bas.ac.uk/uk-pdc/metadata-infrastructure/metadata-generator/issues/50) |\n\n**Note:** In this library, the *ISO 19115:2003* standard is referred to as *ISO-19115-1* (`iso_19115_1_v1`) for\nconsistency with *ISO 19115-2:2009* (referred to as *ISO-19115-2*, `iso_19115_2_v1`). In the future, the\n[ISO 19115-1:2014](https://www.iso.org/standard/53798.html) standard will be referred to as *ISO-19115-3*.\n\n### Supported profiles\n\n| Standard | Profile  | Implementation  | Library Namespace | Introduced In |\n| -------- | -------- | --------------- | ----------------- | ------------- |\n| -        | -        | -               | -                 | -             |\n\n**Note:** Support for profiles has been removed to allow underlying standards to be implemented more easily, and to \nwait until stable profiles for UK PDC Discovery metadata have been developed and approved.\n\n### Supported configuration versions\n\n| Standard         | Profile | Configuration Version                                                                                                 | Status     | Notes                               |\n| ---------------- | ------- | --------------------------------------------------------------------------------------------------------------------- | ---------- | ----------------------------------- |\n| ISO 19115:2003   | -       | [`v1`](https://metadata-standards.data.bas.ac.uk/bas-metadata-generator-configuration-schemas/v2/iso-19115-1-v1.json) | Deprecated | Deprecated version replaced by `v2` |\n| ISO 19115:2003   | -       | [`v2`](https://metadata-standards.data.bas.ac.uk/bas-metadata-generator-configuration-schemas/v2/iso-19115-1-v2.json) | Live       | Stable version                      |\n| ISO 19115-2:2009 | -       | [`v1`](https://metadata-standards.data.bas.ac.uk/bas-metadata-generator-configuration-schemas/v2/iso-19115-2-v1.json) | Deprecated | Deprecated version replaced by `v2` |\n| ISO 19115-2:2009 | -       | [`v2`](https://metadata-standards.data.bas.ac.uk/bas-metadata-generator-configuration-schemas/v2/iso-19115-2-v2.json) | Live       | Stable version                      |\n\n## Installation\n\nThis package can be installed using Pip from [PyPi](https://pypi.org/project/bas-metadata-library):\n\n```\n$ pip install bas-metadata-library\n```\n\n## Usage\n\n### Encode XML document from record configuration\n\nTo generate an ISO 19115 metadata record from a Python record configuration and return it as an XML document:\n\n```python\nfrom datetime import date\nfrom bas_metadata_library.standards.iso_19115_2 import MetadataRecordConfigV2, MetadataRecord\n\nminimal_record_config = {\n    "hierarchy_level": "dataset",\n    "metadata": {\n        "language": "eng",\n        "character_set": "utf-8",\n        "contacts": [{"organisation": {"name": "UK Polar Data Centre"}, "role": ["pointOfContact"]}],\n        "date_stamp": date(2018, 10, 18),\n    },\n    "identification": {\n        "title": {"value": "Test Record"},\n        "dates": {"creation": {"date": date(2018, 1, 1), "date_precision": "year"}},\n        "abstract": "Test Record for ISO 19115 metadata standard (no profile) with required properties only.",\n        "character_set": "utf-8",\n        "language": "eng",\n        "topics": ["environment", "climatologyMeteorologyAtmosphere"],\n        "extent": {\n            "geographic": {\n                "bounding_box": {\n                    "west_longitude": -45.61521,\n                    "east_longitude": -27.04976,\n                    "south_latitude": -68.1511,\n                    "north_latitude": -54.30761,\n                }\n            }\n        },\n    },\n}\nconfiguration = MetadataRecordConfigV2(**minimal_record_config)\nrecord = MetadataRecord(configuration=configuration)\ndocument = record.generate_xml_document()\n\n# output document\nprint(document.decode())\n```\n\n#### Loading a record configuration from JSON\n\nThe `load()` and `loads()` methods on the configuration class can be used to load a record configuration encoded as a\nJSON file or JSON string respectively:\n\n```python\nfrom pathlib import Path\n\nfrom bas_metadata_library.standards.iso_19115_2 import MetadataRecordConfigV2\n\nconfiguration = MetadataRecordConfigV2()\nconfiguration.load(file=Path("/path/to/file.json"))\n```\n\n```python\nfrom bas_metadata_library.standards.iso_19115_2 import MetadataRecordConfigV2\n\nconfiguration = MetadataRecordConfigV2()\nconfiguration.loads(string=\'{"file_identifier": "696770d9-7cd8-40f0-b269-11af1687c772"}\')\n```\n\n#### Disabling XML declaration\n\nTo disable the XML declaration (i.e. `<?xml version=\'1.0\' encoding=\'utf-8\'?>`), you can set the `xml_declaration` \nparameter to false. This is sometimes needed when the generated XML documented needs to be embedded into a larger \ndocument, such as a CSW transaction.\n\n```python\n# disable XML declaration\ndocument = record.generate_xml_document(xml_declaration=False)\n\n# output document\nprint(document)\n```\n\n### Decode record configuration from XML document\n\nTo reverse this process and convert a XML record into a configuration object:\n\n```python\nfrom bas_metadata_library.standards.iso_19115_2 import MetadataRecord\n\nwith open(f"minimal-record.xml") as record_file:\n    record_data = record_file.read()\n\nrecord = MetadataRecord(record=record_data)\nconfiguration = record.make_config()\nminimal_record_config = configuration.config\n\n# output configuration\nprint(minimal_record_config)\n```\n\n### Migrating to new configuration versions\n\n#### Version 1 to version 2\n\n**Note:** The version 1 configuration schema is deprecated and will be removed in the next version \n[#116](https://gitlab.data.bas.ac.uk/uk-pdc/metadata-infrastructure/metadata-generator/-/issues/116).\n\nUtility methods are provided within the V1 and V2 [Record configuration](#configuration-classes) classes to convert to\nand from the V2/V1 [Record Configuration Schema](#configuration-schemas).\n\n**Note:** The version 1 and version 2 schemas are largely, but not fully, backwards compatible. Additional elements \nadded to the version 2 schema (i.e. for elements the version 1 schema didn\'t support) will be dropped to prevent \nvalidation errors. For some elements (access/usage constraints), hard coded conversions are used for known use cases.\n\nTo convert a record configuration from version 1 to version 2 (lossless for known use cases):\n\n```python\nfrom datetime import date\n\nfrom bas_metadata_library.standards.iso_19115_1 import (\n    MetadataRecordConfigV1 as ISO19115_1_MetadataRecordConfigV1,\n    MetadataRecord as ISO19115_1_MetadataRecord,\n)\n\nconfiguration_object = {\n    "language": "eng",\n    "character_set": "utf-8",\n    "hierarchy_level": "dataset",\n    "contacts": [{"organisation": {"name": "UK Polar Data Centre"}, "role": ["pointOfContact"]}],\n    "date_stamp": date(2018, 10, 18),\n    "resource": {\n        "title": {"value": "Test Record"},\n        "dates": [{"date": date(2018, 1, 1), "date_precision": "year", "date_type": "creation"}],\n        "abstract": "Test Record for ISO 19115 metadata standard (no profile) with required properties only.",\n        "character_set": "utf-8",\n        "language": "eng",\n        "topics": ["environment", "climatologyMeteorologyAtmosphere"],\n        "extent": {\n            "geographic": {\n                "bounding_box": {\n                    "west_longitude": -45.61521,\n                    "east_longitude": -27.04976,\n                    "south_latitude": -68.1511,\n                    "north_latitude": -54.30761,\n                }\n            }\n        },\n    },\n}\nconfigurationV1 = ISO19115_1_MetadataRecordConfigV1(**configuration_object)\nconfigurationV2 = configurationV1.convert_to_v2_configuration()\n\n# encode converted configuration into an XML document\nrecord = ISO19115_1_MetadataRecord(configurationV2)\ndocument = record.generate_xml_document()\n\n# output document\nprint(document)\n```\n\nTo convert a record configuration from version 2 to version 1 (lossy):\n\n```python\nfrom datetime import date\n\nfrom bas_metadata_library.standards.iso_19115_1 import (\n    MetadataRecordConfigV2 as ISO19115_1_MetadataRecordConfigV2,\n    MetadataRecordConfigV1 as ISO19115_1_MetadataRecordConfigV1\n)\n\nconfiguration_object = {\n    "hierarchy_level": "dataset",\n    "metadata": {\n        "language": "eng",\n        "character_set": "utf-8",\n        "contacts": [{"organisation": {"name": "UK Polar Data Centre"}, "role": ["pointOfContact"]}],\n        "date_stamp": date(2018, 10, 18),\n    },\n    "identification": {\n        "title": {"value": "Test Record"},\n        "dates": {"creation": {"date": date(2018, 1, 1), "date_precision": "year"}},\n        "abstract": "Test Record for ISO 19115 metadata standard (no profile) with required properties only.",\n        "character_set": "utf-8",\n        "language": "eng",\n        "topics": ["environment", "climatologyMeteorologyAtmosphere"],\n        "extent": {\n            "geographic": {\n                "bounding_box": {\n                    "west_longitude": -45.61521,\n                    "east_longitude": -27.04976,\n                    "south_latitude": -68.1511,\n                    "north_latitude": -54.30761,\n                }\n            }\n        },\n    },\n}\nconfigurationV2 = ISO19115_1_MetadataRecordConfigV2(**configuration_object)\nconfigurationV1 = ISO19115_1_MetadataRecordConfigV1()\nconfigurationV1.convert_from_v2_configuration(configuration=configurationV2)\n\n# print V1 configuration\nprint(configurationV1.config)\n```\n\n### HTML entities\n\nDo not include HTML entities in input to this generator, as they will be double escaped by [Lxml](https://lxml.de), the\nunderlying XML processing library used by this project. Instead, literal characters should be used (e.g. `>`), which\nwill be escaped as needed automatically. This applies to any unicode character, such as accents (e.g. `å`) and\nsymbols (e.g. `µ`).\n\nE.g. If `&gt;`, the HTML entity for `>` (greater than), were used as input, it would be escaped again to `&amp;gt;`\nwhich will not be valid output.\n\n### Linking transfer options and formats\n\nTo support generating a table of download options for a resource (such as [1]), this library uses a \'distribution\noption\' concept to group related formats and transfer option elements in [Record Configurations](#configuration-classes).\n\nIn ISO these elements are independent of each other, with no formal mechanism to associate formats and transfer options.\nAs this library seeks to be fully reversible between a configuration object and XML, this information would be lost\nonce records are encoded as XML.\n\nTo avoid this, this library uses the ID attribute available in both format and transfer option elements with values\ncan be used when decoding XML to reconstruct these associations. This functionality should be fully transparent to the\nuser, except for these auto-generated IDs being present in records.\n\nSee the [Automatic transfer option / format IDs](#automatic-transfer-option--format-ids) section for more details.\n\n**Note:** Do not modify these IDs, as this will break this functionality.\n\n[1]\n\n| Format     | Size   | Download Link                |\n| ---------- | ------ | ---------------------------- |\n| CSV        | 68 kB  | [Link](https://example.com/) |\n| GeoPackage | 1.2 MB | [Link](https://example.com/) |\n\n## Implementation\n\nThis library is implemented in Python and consists of a set of classes used to generate XML metadata records from a\nconfiguration object, or to generate a configuration object from an XML record.\n\n### Metadata Record classes\n\nEach [supported Standard](#supported-standards) and [Supported Profile](#supported-profiles) is implemented as a module\nunder `bas_metadata_library.standards` (where profiles are implemented as modules under their respective standard).\n\nFor each, classes inherited from these parent classes are defined:\n\n* `Namespaces`\n* `MetadataRecord`\n* `MetadataRecordConfig`\n\nThe `namespaces` class is a set of mappings between XML namespaces, their shorthand aliases and their definitions XSDs.\n\nThe `MetadataRecord` class represents a metadata record and defines the Root [Element](#element-classes). This class\nprovides methods to generate an XML document for example.\n\nThe `MetadataRecordConfig` class represents the [Configuration](#configuration-classes) used to define values within a\n`MetadataRecord`, either for new records, or derived from existing records. This class provides methods to validate the\nconfiguration used in a record for example.\n\n### Element classes\n\nEach supported element, in each [supported standard](#supported-standards), inherit and use the `MetadataRecordElement`\nclass to:\n\n* encode configuration values into an XML fragment of at least one element\n* decode an XML fragment into one or more configuration values\n\nSpecifically, at least two methods are implemented:\n\n* `make_element()` which builds an XML element using values from a configuration object\n* `make_config()` which uses typically XPath expressions to build a configuration object from XML\n\nThese methods may be simple (if encoding or decoding a simple free text value for example), or quite complex through\nthe use of sub-elements (which themselves may contain sub-elements as needed).\n\n### Configuration classes\n\nThe configuration of each metadata record is held in a Python dictionary, within a `MetadataRecordConfig` class. This\nclass includes methods to validate its configuration against a relevant [Configuration Schema](#configuration-schemas).\n\nConfiguration classes are defined at the root of each standard or profile, alongside its root\n[Metadata Element](#element-classes) and XML namespaces.\n\nA configuration class will exist for each supported configuration schema with methods to convert from one version to\nanother, see the [Record configuration schema migration](#migrating-to-new-configuration-versions) section for more \ninformation.\n\n### Configuration schemas\n\nAllowed configuration values for each [supported Standard](#supported-standards) and\n[Supported Profile](#supported-profiles) are described by a [JSON Schema](https://json-schema.org). These configuration\nschemas include which configuration properties are required, and in some cases, allowed values for these properties.\n\nConfiguration schemas are stored as JSON files in the `bas_metadata_library.standards_schemas` module and loaded as\nresource files from within this package. Schemas are also made available externally through the BAS Metadata Standards\nwebsite, [metadata-standards.data.bas.ac.uk](https://metadata-standards.data.bas.ac.uk), to allow:\n\n1. other applications to ensure their output will be compatible with this library but that can\'t, or don\'t want to,\n   use this library\n2. to allow schema inheritance/extension where used for standards that inherit from other standards (such as profiles)\n\nConfiguration schemas a versioned (e.g. `v1`, `v2`) to allow for backwards incompatible changes to be made.\n\n#### Source and distribution schemas\n\nStandards and profiles usually inherit from other standards and profiles. In order to prevent this creating huge\nduplication within configuration schemas, inheritance is used to incorporate a base schema and extend it as needed. For\nexample, the ISO 19115-2 standard extends, and therefore incorporates the configuration schema for, ISO 19115-1.\n\nJSON Schema references and identifier properties are used to implement this, using URIs within the BAS Metadata\nStandards website. Unfortunately, this creates a problem when developing these schemas, as if Schema B relies on Schema\nA, using its published identifier as a reference, the published instance of the schema will be used (i.e. the remote\nschema will be downloaded when Schema B is validated). If Schema A is being developed, and is not ready to be\nrepublished, there is a difference between the local and remote schemas used, creating unreliable tests for example.\n\nTo avoid this problem, a set of *source* schemas are used which use references to avoid duplication, from which a set\nof *distribution* schemas are generated. These distribution schemas inline any references contained in their source\ncounterpart. These distribution schemas are therefore self-contained and can be updated locally without any\ndependencies on remote sources. Distribution schemas are used by [Configuration Classes](#configuration-classes) and\npublished to the BAS Metadata Standards website, they are located in the `bas_metadata_library.schemas.dist` module.\n\nWhen editing configuration schemas, you should edit the source schemas, located in the\n`bas_metadata_library.schemas.src` module, then run the\n[regenerate distribution schemas](#generating-configuration-schemas) using an internal command line utility.\n\nJSON Schema\'s can be developed using [jsonschemavalidator.net](https://www.jsonschemavalidator.net).\n\n### Adding a new standard\n\nTo add a new standard:\n\n1. create a new module under `bas_metadata_library.standards`, e.g. `bas_metadata_library.standards.foo_v1/__init__.py`\n2. in this module, overload the `Namespaces`, `MetadataRecordConfig` and `MetadataRecord` classes as needed\n3. create a suitable metadata configuration JSON schema in `bas_metadata_library.standards_schemas/`\n   e.g. `bas_metadata_library.standards_schemas/foo_v1/configuration-schema.json`\n4. add a script line to the `publish-schemas-stage` and `publish-schemas-prod` jobs in `.gitlab-ci.yml`, to publish\n   the configuration schema within the BAS Metadata Standards website\n5. define a series of test configurations (e.g. minimal, typical and complete) for generating test records in\n   `tests/resources/configs/` e.g. `tests/resources/configs/foo_v1_standard.py`\n6. update the inbuilt Flask application in `app.py` with a route for generating test records for the new standard\n7. use the inbuilt Flask application to generate the test records and save to `tests/resources/records/`\n8. add relevant [tests](#testing) with methods to test each metadata element class and test records\n\n### Adding a new element to an existing standard\n\n[WIP]\n\n1. [amend configuration schema](#configuration-schemas):\n   * new or changed properties should be added to the configuration for the relevant standard (e.g. ISO 19115-1)\n   * typically, this involves adding new elements to the `definitions` property and referencing these in the relevant \n     parent element (e.g. to the `identification` property)\n2. [generate distribution schemas](#generating-configuration-schemas)\n3. amend test configs:\n   * new or changed properties should be made to the relevant test record configurations in `tests/resources/configs/`\n   * there are different levels of configuration, from minimal to complete, which should, where possible, build on \n     each other (e.g. the complete record should include all the properties and values of the minimal record)\n   * the `minimum` configuration should not be changed, as all mandatory elements are already implemented\n   * the `base_simple` configuration should contain elements used most of the time, that use free-text values\n   * the `base_complex` configuration should contain elements used most of the time, that use URL or other \n     identifier values\n   * the `complete` configuration should contain examples of all supported elements, providing this still produces a \n     valid record, in order to ensure high test coverage\n   * where possible, configurations should be internally consistent, but this can be ignored if needed\n   * values used for identifiers and other external references should use the correct form/structure but do not need \n     to exist or relate to the resource described by each configuration (i.e. DOIs should be valid URLs but could be \n     a DOI for another resource for example)\n4. add relevant [element class](#element-classes):\n   * new or changed elements should be added to the configuration for the relevant package for each standard\n   * for the ISO 19115 family of standards, element classes should be added to the `iso_19115_common` package\n   * the exact module to use within this package will depend on the nature of the element being added, but in general, \n     elements should be added to the module of their parent element (e.g. `data_identification.py` for elements \n     under the `identification` record configuration property), elements used across a range of elements should be \n     added to the `common_elements.py` module\n   * remember to include references to new element class in the parent element class (in both the `make_element` and \n     `make_config` methods)\n5. until support for Version 1 configuration schemas is removed, add logic to the \n   `bas_metadata_library.standards.iso_19115_common.utils.convert_from_v1_to_v2_configuration` and/or\n   `bas_metadata_library.standards.iso_19115_common.utils.convert_from_v2_to_v1_configuration` methods as needed\n   * for new elements, this usually consists of deleting configuration properties that don\'t exist in the V1 schema \n     (as additional/unexpected keys are not allowed and will therefore fail validation)\n   * for existing elements, logic may be needed to both upgrade and downgrade configurations, especially where \n     refactoring has occurred between V1 and V2 configurations\n   * where possible, such logic should be generic and agnostic to values used for configuration options, however \n     there may be cases where this is unavoidable in order to produce a more complete translation between versions\n   * if such logic would prove very unwieldy, and not confined to a limited set of known circumstances, it is ok to \n     not implement such logic, on the basis that supporting multiple versions is temporary\n6. [capture test records](#capturing-static-test-records)\n    * initially this acts as a good way to check new or changed element classes encode configuration properties \n      correctly\n    * check the git status of these test records to check existing records have changed how you expect (and haven\'t \n      changed things you didn\'t intend to for example)\n7. add tests\n    * new test cases should be added, or existing test cases updated, in the relevant module within \n      `tests/bas_metadata_library/`\n    * for the ISO 19115 family of standards, this should be `test_standard_iso_19115_1.py`, unless the element is only\n      part of the ISO 19115-2 standard\n    * providing there are enough test configurations to test all the ways a new element can be used (e.g. with a simple\n      text string or anchor element for example), adding a test case for each element is typically enough to ensure \n      sufficient test coverage\n    * where this isn\'t the case, it\'s suggested to add one or more \'edge case\' test cases to test remaining code paths\n      explicitly\n8. check [test coverage](#test-coverage)\n    * for missing coverage, consider adding edge case test cases where applicable\n    * wherever possible, the coverage exemptions should be minimised\n    * there are a number of general types of code that can be exempted as part of an existing convention (but that \n      will be reviewed in the future):\n        * within `make_config` methods to check whether child elements are empty\n        * within the `convert_from_v1_to_v2_configuration` and `convert_from_v2_to_v1_configuration` utility methods\n    * where exceptions are added, they should be documented as an issue with information on how they will be \n      addressed in the longer term\n    * issue \n      [#111](https://gitlab.data.bas.ac.uk/uk-pdc/metadata-infrastructure/metadata-generator/-/issues/111)) \n      will document existing exceptions and conventions, and look at how these can be removed in the future\n9. update `README.md` examples if common element\n    * this is probably best done before releasing a new version\n10. update `CHANGELOG.md`\n11. if needed, add name to `authors` property in `pyproject.toml`\n\n### Automatic transfer option / format IDs\n\nID attributes are automatically added to `gmd:MD_Format` and `gmd:MD_DigitalTransferOptions` elements in order to\nreconstruct related formats and transfer options (see the\n[Linking transfer options and formats](#linking-transfer-options-and-formats) section for more information).\n\nWhen a record is encoded, ID values are generated by hashing a JSON encoded string of the distribution object. This\nID is used as a shared base between the format and transfer option, with `-fmt` appended for the format and `-tfo`\nfor the transfer option.\n\nWhen a record is decoded, ID values are extracted (stripping the `-fmt`/`-tfo` suffixes) to index and then match up\nformat and transfer options back into distribution options. Any format and transfer options without an ID value, or\nwithout a corresponding match, are added as partial distribution options.\n\nAs a worked example for encoding a (simplified) distribution object such as:\n\n```python\ndo = {\n   \'format\': \'csv\',\n   \'transfer_option\': {\n      \'size\': \'40\',\n      \'url\': \'https://example.com/foo.csv\'\n   }\n}\n```\n\nBecomes:\n\n```\n\'{"format":"csv","transfer_option":{"size":40,"url":"https://example.com/foo.csv"}}\'\n```\n\nWhen encoded as a JSON encoded string, which when hashed becomes:\n\n```\n16b7b5df78a664b15d69feda7ccc7caed501f341\n```\n\nThe ID value added to the `gmd:MD_Format` element would be:\n\n```xml\n<gmd:MD_Format id="16b7b5df78a664b15d69feda7ccc7caed501f341-fmt">\n```\n\nAnd for the `gmd:MD_DigitalTransferOptions` element:\n\n```xml\n<gmd:MD_DigitalTransferOptions id="16b7b5df78a664b15d69feda7ccc7caed501f341-tfo">\n```\n\n## Setup\n\n### Terraform\n\nTerraform is used to provision resources required to operate this application in staging and production environments.\n\nThese resources allow [Configuration schemas](#configuration-schemas) for each standard to be accessed externally.\n\nAccess to the [BAS AWS account](https://gitlab.data.bas.ac.uk/WSF/bas-aws) is needed to provisioning these resources.\n\n**Note:** This provisioning should have already been performed (and applies globally). If changes are made to this\nprovisioning it only needs to be applied once.\n\n```shell\n# start terraform inside a docker container\n$ cd provisioning/terraform\n$ docker compose run terraform\n# setup terraform\n$ terraform init\n# apply changes\n$ terraform validate\n$ terraform fmt\n$ terraform apply\n# exit container\n$ exit\n$ docker compose down\n```\n\n#### Terraform remote state\n\nState information for this project is stored remotely using a\n[Backend](https://www.terraform.io/docs/backends/index.html).\n\nSpecifically the [AWS S3](https://www.terraform.io/docs/backends/types/s3.html) backend as part of the\n[BAS Terraform Remote State](https://gitlab.data.bas.ac.uk/WSF/terraform-remote-state) project.\n\nRemote state storage will be automatically initialised when running `terraform init`. Any changes to remote state will\nbe automatically saved to the remote backend, there is no need to push or pull changes.\n\n##### Remote state authentication\n\nPermission to read and/or write remote state information for this project is restricted to authorised users. Contact\nthe [BAS Web & Applications Team](mailto:servicedesk@bas.ac.uk) to request access.\n\nSee the [BAS Terraform Remote State](https://gitlab.data.bas.ac.uk/WSF/terraform-remote-state) project for how these\npermissions to remote state are enforced.\n\n## Development\n\nThis API is developed as a Python library. A bundled Flask application is used to simulate its usage, act as\nframework for running tests etc., and provide utility methods for generating schemas etc.\n\n### Development environment\n\nGit, Docker and Docker Compose are required to set up a local development environment of this application.\n\nIf you have access to the [BAS GitLab instance](https://gitlab.data.bas.ac.uk), you can clone the project and pull\nDocker images from the BAS GitLab instance and BAS Docker Registry.\n\n```shell\n$ git clone https://gitlab.data.bas.ac.uk/uk-pdc/metadata-infrastructure/metadata-generator.git\n$ cd metadata-generator\n$ docker login docker-registry.data.bas.ac.uk\n$ docker compose pull\n```\n\nOtherwise, you will need to build the Docker image locally.\n\n```shell\n$ git clone https://github.com/antarctica/metadata-library.git\n$ cd metadata-library\n$ docker compose build\n```\n\nTo run the application using the Flask development server (which reloads automatically if source files are changed):\n\n```shell\n$ docker compose up\n```\n\nTo run other commands against the Flask application (such as [Tests](#testing)):\n\n```shell\n# in a separate terminal to `docker compose up`\n$ docker compose run app flask [command]\n# E.g.\n$ docker compose run app flask test\n# List all available commands\n$ docker compose run app flask\n```\n\n### Code Style\n\nPEP-8 style and formatting guidelines must be used for this project, with the exception of the 80 character line limit.\n\n[Black](https://github.com/psf/black) is used to ensure compliance, configured in `pyproject.toml`.\n\nBlack can be [integrated](https://black.readthedocs.io/en/stable/editor_integration.html#pycharm-intellij-idea) with a\nrange of editors, such as PyCharm, to perform formatting automatically.\n\nTo apply formatting manually:\n\n```shell\n$ docker compose run app black bas_metadata_library/\n```\n\nTo check compliance manually:\n\n```shell\n$ docker compose run app black --check bas_metadata_library/\n```\n\nChecks are ran automatically in [Continuous Integration](#continuous-integration).\n\n### Dependencies\n\nPython dependencies for this project are managed with [Poetry](https://python-poetry.org) in `pyproject.toml`.\n\nNon-code files, such as static files, can also be included in the [Python package](#python-package) using the\n`include` key in `pyproject.toml`.\n\n#### Adding new dependencies\n\nTo add a new (development) dependency:\n\n```shell\n$ docker compose run app ash\n$ poetry add [dependency] (--dev)\n```\n\nThen rebuild the development container, and if you can, push to GitLab:\n\n```shell\n$ docker compose build app\n$ docker compose push app\n```\n\n#### Updating dependencies\n\n```shell\n$ docker compose run app ash\n$ apk update\n$ apk add build-base cargo\n$ poetry update\n```\n\nThen rebuild the development container, and if you can, push to GitLab:\n\n```shell\n$ docker compose build app\n$ docker compose push app\n```\n\n#### `jsonschema` package\n\nThe `jsonschema` dependency is locked to version 3.2.0 because version 4.0 > dropped Python 3.6 support. This \nlibrary cannot require newer Python versions to ensure it can be used in projects that run on BAS IT infrastructure.\n\n#### `lxml` package\n\nThe `lxml` dependency takes a long time to install/update inside the container image because it needs to be installed \nfrom source each time the container is built. This is because Alpine Linux, used by the official Python Docker base \nimages, is not supported by the Python [manylinux](https://github.com/pypa/manylinux) system, and therefore cannot use\npre-built, binary, wheels.\n\n### Static security scanning\n\nTo ensure the security of this API, source code is checked against [Bandit](https://github.com/PyCQA/bandit) for issues\nsuch as not sanitising user inputs or using weak cryptography.\n\n**Warning:** Bandit is a static analysis tool and can\'t check for issues that are only be detectable when running the\napplication. As with all security tools, Bandit is an aid for spotting common mistakes, not a guarantee of secure code.\n\nThrough [Continuous Integration](#continuous-integration), each commit is tested.\n\nTo check locally:\n\n```shell\n$ docker compose run app bandit -r . -x \'./tests\'\n```\n\n### Editor support\n\n#### PyCharm\n\nA run/debug configuration, *App*, is included in the project.\n\n### Generating configuration schemas\n\nTo generate [distribution schemas from source schemas](#source-and-distribution-schemas), a custom Flask CLI command,\n`generate-schemas` is available. The [`jsonref`](https://jsonref.readthedocs.io/en/latest/) library is used to resolve\nany references in source schemas and write the output as distribution schemas, replacing any existing output.\n\n```shell\n# then in a separate terminal:\n$ docker compose run app flask generate-schemas\n```\n\nTo configure this command, (e.g. to add a new schema for a new standard/profile), adjust the `schemas` list in the\n`generate_schemas` method in `manage.py`. This list should contain dictionaries with keys for the common name of the\nschema (based on the common file name of the schema JSON file), and whether the source schema should be resolved or\nsimply copied. This should be true by default, and is only relevant to schemas that do not contain any references, as\nthis will cause an error if resolved.\n\n## Testing\n\nAll code in the `bas_metadata_library` module must be covered by tests, defined in `tests/`. This project uses\n[PyTest](https://docs.pytest.org/en/latest/) which should be ran in a random order using\n[pytest-random-order](https://pypi.org/project/pytest-random-order/).\n\nTests are written to create metadata records based on a series of configurations defined in `tests/resources/configs/`. \nThese define \'minimal\' to \'complete\' test records, intended to test different ways a standard can be used, both for\nindividual elements and whole records. These tests are designed to ensure that records are generally well-formed and\nthat where config options are used the corresponding elements in the metadata record are generated.\n\nAs this library does not seek to support all possible elements and variations within each standard, these tests are\nsimilarly not exhaustive, nor are they a substitute for formal metadata validation.\n\nTest methods are used to test individual elements are formed correctly. Comparisons against static records are used to\ntest the structure of whole records.\n\nTo run tests manually from the command line:\n\n```shell\n$ docker compose run app pytest --random-order\n```\n\nTo run tests manually using PyCharm, use the included *App (Tests)* run/debug configuration.\n\nTests are ran automatically in [Continuous Integration](#continuous-integration).\n\n### Capturing static test records\n\nTo capture static test records, which verify complete records are assembled correctly, a custom Flask CLI command,\n`capture-test-records` is available. This requires the Flask application to first be running. The Requests library is\nused to make requests against the Flask app save responses to a relevant directory in `tests/resources/records`.\n\n```shell\n# start Flask application:\n$ docker compose up\n# then in a separate terminal:\n$ docker compose run app flask capture-test-records\n```\n\nIt is intended that this command will update pre-existing static records, with differences captured in version control\nand reviewed manually to ensure they are correct.\n\n### Test coverage\n\n[pytest-cov](https://pypi.org/project/pytest-cov/) is used to measure test coverage.\n\nTo prevent noise, `.coveragerc` is used to omit empty `__init__.py` files from reports.\n\nTo measure coverage manually:\n\n```shell\n$ docker compose run app pytest --random-order --cov=bas_metadata_library --cov-fail-under=100 --cov-report=html .\n```\n\n[Continuous Integration](#continuous-integration) will check coverage automatically and fail if less than 100%.\n\n### Continuous Integration\n\nAll commits will trigger a Continuous Integration process using GitLab\'s CI/CD platform, configured in `.gitlab-ci.yml`.\n\n## Deployment\n\n### Python package\n\nThis project is distributed as a Python package, hosted in [PyPi](https://pypi.org/project/bas-metadata-library).\n\nSource and binary packages are built and published automatically using\n[Poetry](https://python-poetry.org/docs/cli/#publish) in [Continuous Delivery](#continuous-deployment).\n\n### Continuous Deployment\n\nA Continuous Deployment process using GitLab\'s CI/CD platform is configured in `.gitlab-ci.yml`.\n\n## Release procedure\n\nFor all releases:\n\n1. create a release branch\n2. close release in `CHANGELOG.md`\n3. bump package version using `docker compose run app poetry version`\n4. push changes, merge the release branch into `master` and tag with version\n\n## Feedback\n\nThe maintainer of this project is the BAS Web & Applications Team, they can be contacted at:\n[servicedesk@bas.ac.uk](mailto:servicedesk@bas.ac.uk).\n\n## Issue tracking\n\nThis project uses issue tracking, see the\n[Issue tracker](https://gitlab.data.bas.ac.uk/uk-pdc/metadata-infrastructure/metadata-generator/issues) for more\ninformation.\n\n**Note:** Read & write access to this issue tracker is restricted. Contact the project maintainer to request access.\n\n## License\n\n© UK Research and Innovation (UKRI), 2019 - 2021, British Antarctic Survey.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated \ndocumentation files (the "Software"), to deal in the Software without restriction, including without limitation the \nrights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit \npersons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the \nSoftware.\n\nTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE \nWARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR \nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR \nOTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n',
    'author': 'Felix Fennell',
    'author_email': 'felnne@bas.ac.uk',
    'maintainer': None,
    'maintainer_email': None,
    'url': 'https://github.com/antarctica/metadata-library',
    'packages': packages,
    'package_data': package_data,
    'install_requires': install_requires,
    'python_requires': '>=3.6,<4.0',
}


setup(**setup_kwargs)
