diff --git a/.github/workflows/run-tests.yaml b/.github/workflows/run-tests.yaml
index 0d36f04..4f5827b 100644
--- a/.github/workflows/run-tests.yaml
+++ b/.github/workflows/run-tests.yaml
@@ -17,7 +17,13 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
-
+ with:
+ path: "."
+ - name: Checkout hakai metadata filesxz
+ uses: actions/checkout@v4
+ with:
+ repository: HakaiInstitute/hakai-metadata-entry-form-files
+ path: ./tests/records/hakai-metadata-entry-form-files
- name: Set up Python 3.11
uses: actions/setup-python@v5
with:
@@ -34,8 +40,9 @@ jobs:
- name: Run pytest
run: poetry run pytest
- - name: Convert test record to CITATION.cff
+ - name: Convert test record to CITATION.cff if main branch
uses: hakaiinstitute/hakai-metadata-conversion@main
+ if: github.ref == 'refs/heads/main'
with:
input: tests/records/test_record1.json
output-file: CITATION.cff
diff --git a/.gitignore b/.gitignore
index 611d2d8..fe6e2d9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
**/__pycache__
.DS_Store
-tests/records/hakai-metadata-entry-form-files
\ No newline at end of file
+tests/records/hakai-metadata-entry-form-files
+tests/results
\ No newline at end of file
diff --git a/README.md b/README.md
index 8bb3535..9da52e0 100644
--- a/README.md
+++ b/README.md
@@ -58,7 +58,7 @@ job:
step:
- use: action/checkout@v4
- name: Sync metadata
- use: hakaiinsitute/hakai-metadata-conversion
+ use: hakaiinstitute/hakai-metadata-conversion
with:
- input: url or file path within repo
- output-file: CITATION.cff
diff --git a/action.yaml b/action.yaml
index 14e481b..399a587 100644
--- a/action.yaml
+++ b/action.yaml
@@ -67,7 +67,7 @@ runs:
- name: Run conversion
shell: bash
run: |
- hakai_metadata_conversion \
+ hakai_metadata_conversion convert \
--input ${{ inputs.input }} \
--output-dir ${{ inputs.output-dir }} \
--output-file ${{ inputs.output-file }} \
diff --git a/hakai_metadata_conversion/__main__.py b/hakai_metadata_conversion/__main__.py
index dfe96c1..9ec2043 100644
--- a/hakai_metadata_conversion/__main__.py
+++ b/hakai_metadata_conversion/__main__.py
@@ -39,7 +39,7 @@ def load(input, format, encoding="utf-8") -> dict:
return input_formats[format](f)
-def convert(record, format) -> str:
+def converter(record, format) -> str:
"""Run the conversion to the desired format."""
if format == "json":
return json.dumps(record, indent=2)
@@ -53,7 +53,18 @@ def convert(record, format) -> str:
raise ValueError(f"Unknown output format: {format}")
-@click.command()
+@click.group(name="hakai-metadata-conversion")
+def cli():
+ """Hakai Metadata Conversion CLI.
+ Convert metadata records to different metadata formats or standards.
+ """
+ pass
+
+
+cli.add_command(erddap.update, name="erddap-update")
+
+
+@cli.command(name="convert")
@click.option("--input", "-i", required=True, help="Input file.")
@click.option(
"--recursive", "-r", is_flag=True, help="Process files recursively.", default=False
@@ -100,20 +111,21 @@ def convert(record, format) -> str:
show_default=True,
)
@logger.catch(reraise=True)
-def cli_main(**kwargs):
+def cli_convert(**kwargs):
"""Convert metadata records to different metadata formats or standards."""
- main(**kwargs)
+ convert(**kwargs)
-def main(
+@logger.catch(reraise=True)
+def convert(
input,
- recursive,
- input_file_format,
- encoding,
- output_dir,
- output_file,
- output_format,
- output_encoding,
+ output_format: str,
+ recursive: bool = False,
+ input_file_format: str = "yaml",
+ encoding: str = "utf-8",
+ output_dir: str = ".",
+ output_file: str = None,
+ output_encoding: str = "utf-8",
):
"""Convert metadata records to different metadata formats or standards."""
@@ -142,7 +154,7 @@ def main(
continue
logger.debug(f"Converting to {output_format}")
- converted_record = convert(record, output_format)
+ converted_record = converter(record, output_format)
# Generate output file path
if output_dir and not output_file:
@@ -163,4 +175,4 @@ def main(
if __name__ == "__main__":
- cli_main()
+ cli()
diff --git a/hakai_metadata_conversion/citation_cff.py b/hakai_metadata_conversion/citation_cff.py
index 024a424..4f4e1a7 100644
--- a/hakai_metadata_conversion/citation_cff.py
+++ b/hakai_metadata_conversion/citation_cff.py
@@ -10,6 +10,15 @@
from hakai_metadata_conversion.utils import drop_empty_values
+def _get_placeholder(language):
+ if language == "en":
+ return "Not available"
+ elif language == "fr":
+ return "Non disponible"
+ else:
+ return "Not available"
+
+
def _get_country_code(country_name):
if not country_name:
return None
@@ -71,6 +80,58 @@ def get_cff_contact(contact):
)
+def _get_doi(record):
+ if not record["identification"].get("identifier", ""):
+ return []
+ return [
+ {
+ "description": "DOI",
+ "type": "doi",
+ "value": (
+ record["identification"]["identifier"].replace("https://doi.org/", "")
+ if "doi.org" in record["identification"].get("identifier", "")
+ else None
+ ),
+ }
+ ]
+
+
+def _get_ressources(record, language):
+ ressources = []
+ for distribution in record["distribution"]:
+ if not distribution["url"].startswith("http"):
+ logger.warning(f"Invalid URL: {distribution['url']}")
+ continue
+ ressources.append(
+ {
+ "description": ": ".join(
+ [
+ item
+ for item in [
+ distribution.get("name", {}).get(language, ""),
+ distribution.get("description", {}).get(
+ language, _get_placeholder(language)
+ ),
+ ]
+ if item
+ ]
+ ),
+ "type": "url",
+ "value": distribution["url"],
+ }
+ )
+ return ressources
+
+
+def _get_unique_authors(record):
+ authors = []
+ for author in record["contact"]:
+ contact = get_cff_contact(author)
+ if contact not in authors:
+ authors.append(contact)
+ return authors
+
+
def citation_cff(
record,
output_format="yaml",
@@ -90,14 +151,11 @@ def citation_cff(
+ "_"
+ record["metadata"]["identifier"]
)
+
record = {
"cff-version": "1.2.0",
"message": message,
- "authors": [
- get_cff_contact(contact)
- for contact in record["contact"]
- if contact["inCitation"]
- ],
+ "authors": _get_unique_authors(record),
"title": record["identification"]["title"].get(language),
"abstract": record["identification"]["abstract"].get(language),
"date-released": record["metadata"]["dates"]["revision"].split("T")[0],
@@ -113,52 +171,29 @@ def citation_cff(
"value": record["metadata"]["identifier"],
},
{
- "description": "Hakai Metadata record URL",
+ "description": "Metadata record URL",
"type": "url",
"value": resource_url,
},
+ *_get_doi(record),
{
- "description": "Hakai Metadata record DOI",
- "type": "doi",
- "value": (
- record["identification"]["identifier"].replace(
- "https://doi.org/", ""
- )
- if "doi.org" in record["identification"].get("identifier", "")
- else None
- ),
- },
- {
- "description": "Hakai Metadata Form used to generate this record",
+ "description": "Metadata Form used to generate this record",
"type": "url",
"value": record["metadata"]["maintenance_note"].replace(
"Generated from ", ""
),
},
- # Generate ressources links
- *[
- {
- "description": ": ".join(
- [
- item
- for item in [
- distribution.get("name", {}).get(language, ""),
- distribution.get("description", {}).get(language),
- ]
- if item
- ]
- ),
- "type": "url",
- "value": distribution["url"],
- }
- for distribution in record["distribution"]
- ],
- ],
- "keywords": [
- keyword
- for _, group in record["identification"]["keywords"].items()
- for keyword in group.get(language, [])
+ *_get_ressources(record, language=language),
],
+ "keywords": list(
+ set(
+ [
+ keyword
+ for _, group in record["identification"]["keywords"].items()
+ for keyword in group.get(language, [])
+ ]
+ )
+ ),
"license": record["metadata"]["use_constraints"].get("licence", {}).get("code"),
"license-url": record["metadata"]["use_constraints"]
.get("licence", {})
diff --git a/hakai_metadata_conversion/erddap.py b/hakai_metadata_conversion/erddap.py
index 9122fbf..af192d8 100644
--- a/hakai_metadata_conversion/erddap.py
+++ b/hakai_metadata_conversion/erddap.py
@@ -1,5 +1,13 @@
+from glob import glob
+from pathlib import Path
+from typing import Union
+
+import click
import yaml
from loguru import logger
+from lxml import etree
+
+from hakai_metadata_conversion.utils import drop_empty_values
KEYWORDS_PREFIX_MAPPING = {
"default": {
@@ -75,6 +83,17 @@ def _get_contributors(contacts: list, separator=";") -> dict:
}
+@logger.catch(default={})
+def _get_platform(record):
+ if not record.get("platform"):
+ return {}
+ platform = record["platform"]
+ return {
+ "platform": platform[0]["type"],
+ "platform_vocabulary": "http://vocab.nerc.ac.uk/collection/L06/current/",
+ }
+
+
def generate_history(record, language="en"):
"""Generate a history string from a metadata record."""
history = record["metadata"].get("history")
@@ -133,12 +152,16 @@ def global_attributes(
metadata_link = (
base_url
+ + "/dataset/"
+ record["metadata"]["naming_authority"].replace(".", "-")
+ "_"
+ record["metadata"]["identifier"]
)
global_attributes = {
+ "institution": (
+ creator[0].get("organization", {}).get("name") if creator else ""
+ ),
"title": record["identification"]["title"][language],
"summary": record["identification"]["abstract"][language],
"project": ",".join(record["identification"].get("project", [])),
@@ -170,7 +193,7 @@ def global_attributes(
"date_created": record["metadata"]["dates"].get("publication"),
"product_version": record["identification"].get("edition"),
"history": generate_history(record, language),
- "license": record["metadata"]["use_constraints"].get("licence", {}).get("code"),
+ "license": record["metadata"]["use_constraints"].get("licence", {}).get("url"),
**(_get_contact(creator[0], "creator") if creator else {}),
**(_get_contact(publisher[0], "publisher") if publisher else {}),
**_get_contributors(record["contact"]),
@@ -180,8 +203,164 @@ def global_attributes(
"metadata_form": record["metadata"]
.get("maintenance_note", "")
.replace("Generated from ", ""),
+ **_get_platform(record),
}
+ # Remove empty values
+ global_attributes = drop_empty_values(global_attributes)
+
if not output:
return global_attributes
if output == "xml":
return generate_dataset_xml(global_attributes)
+
+
+@logger.catch(reraise=True)
+def update_dataset_id(tree, dataset_id: str, global_attributes: dict):
+
+ # Retrive dataset
+ matching_dataset = tree.xpath(f"//dataset[@datasetID='{dataset_id}']")
+ if not matching_dataset:
+ return tree
+
+ # No duplicate dataset IDs allowed
+ if len(matching_dataset) > 1:
+ raise ValueError(f"Duplicate dataset ID {dataset_id} found in XML.")
+ dataset = matching_dataset[0]
+
+ for name, value in global_attributes.items():
+ # Check if the attribute already exists
+ matching_attribute = dataset.xpath(f".//addAttributes/att[@name='{name}']")
+ if matching_attribute:
+ logger.debug(f"Updating attribute {name} with value {value}")
+ matching_attribute[0].text = value
+ else:
+ # Create a new attribute
+ logger.debug(f"Adding new attribute {name} with value {value}")
+ new_attribute = etree.Element("att")
+ new_attribute.text = value
+ new_attribute.attrib["name"] = name
+ dataset.find(".//addAttributes").append(new_attribute)
+
+ return tree
+
+
+# Function to update XML
+@logger.catch(reraise=True)
+def _update_xml(xml_file, dataset_id, updates, encoding="utf-8") -> str:
+ # Parse the XML with comments
+ tree = etree.parse(xml_file)
+ tree = update_dataset_id(tree, dataset_id, updates)
+ # Write back to the same file (or use a different file name to save a new version.
+ return etree.tostring(tree, pretty_print=True).decode(encoding)
+
+
+def _get_dataset_id_from_record(record, erddap_url):
+ for ressource in record["distribution"]:
+ if erddap_url in ressource["url"]:
+ return ressource["url"].split("/")[-1].replace(
+ ".html", ""
+ ), global_attributes(record, output=None)
+ return None, None
+
+
+class ERDDAP:
+ def __init__(self, path) -> None:
+ self.path = path
+ self.tree = None
+
+ self.read()
+
+ def read(self):
+ self.tree = etree.parse(self.path)
+
+ def tostring(self, encoding="utf-8") -> str:
+ return etree.tostring(self.tree, pretty_print=True).decode(encoding)
+
+ def save(self, output_file=None, encoding="utf-8"):
+ with open(output_file or self.path, "w") as f:
+ f.write(self.tostring(encoding))
+
+ def has_dataset_id(self, dataset_id) -> bool:
+ return bool(self.tree.xpath(f"//dataset[@datasetID='{dataset_id}']"))
+
+ def update(self, dataset_id: str, global_attributes: dict):
+
+ # Retrive dataset
+ matching_dataset = self.tree.xpath(f"//dataset[@datasetID='{dataset_id}']")
+ if not matching_dataset:
+ return
+
+ # No duplicate dataset IDs allowed
+ if len(matching_dataset) > 1:
+ raise ValueError(f"Duplicate dataset ID {dataset_id} found in XML.")
+ dataset = matching_dataset[0]
+
+ for name, value in global_attributes.items():
+ # Check if the attribute already exists
+ matching_attribute = dataset.xpath(f".//addAttributes/att[@name='{name}']")
+ if matching_attribute:
+ logger.debug(f"Updating attribute {name} with value {value}")
+ matching_attribute[0].text = value
+ else:
+ # Create a new attribute
+ logger.debug(f"Adding new attribute {name} with value {value}")
+ new_attribute = etree.Element("att")
+ new_attribute.text = value
+ new_attribute.attrib["name"] = name
+ dataset.find(".//addAttributes").append(new_attribute)
+
+ return
+
+
+def update_dataset_xml(
+ datasets_xml: str,
+ records: Union[str, list],
+ erddap_url: str,
+ output_dir: str = None,
+):
+ """Update an ERDDAP dataset.xml with new global attributes."""
+
+ # Find dataset xml
+ if isinstance(records, str):
+ record_files = glob(records, recursive=True)
+ records = [
+ yaml.safe_load(Path(record_file).read_text())
+ for record_file in record_files
+ ]
+
+ # Find dataset xml
+ erddap_files = glob(datasets_xml, recursive=True)
+ if not erddap_files:
+ assert ValueError(f"No files found in {datasets_xml}")
+
+ datasets = [_get_dataset_id_from_record(record, erddap_url) for record in records]
+ dataset_ids = [dataset_id for dataset_id, _ in datasets]
+ updated = []
+ for file in erddap_files:
+ erddap = ERDDAP(file)
+ for dataset_id, attrs in datasets:
+ if not dataset_id:
+ continue
+ if erddap.has_dataset_id(dataset_id):
+ # Update the XML
+ erddap.update(dataset_id, attrs)
+ updated += [dataset_id]
+ file_output = Path(output_dir) / Path(file).name if output_dir else file
+ logger.debug("Writing updated XML to {}", file_output)
+ erddap.save(file_output or file)
+
+ if missing_datasets := [
+ dataset_id for dataset_id in dataset_ids if dataset_id not in updated
+ ]:
+ logger.warning(f"Dataset ID {missing_datasets} not found in {datasets_xml}.")
+ return updated
+
+
+@click.command()
+@click.option("--datasets-xml", "-d", required=True, help="ERDDAP dataset.xml file.")
+@click.option("--records", "-r", required=True, help="Metadata records.")
+@click.option("--erddap-url", "-u", required=True, help="ERDDAP base URL.")
+@click.option("--output-dir", "-o", help="Output directory.")
+def update(datasets_xml, records, erddap_url, output_dir):
+ """Update ERDDAP dataset xml with metadata records."""
+ update_dataset_xml(datasets_xml, records, erddap_url, output_dir)
diff --git a/hakai_metadata_conversion/xml.py b/hakai_metadata_conversion/xml.py
index 8fa662f..497ca1f 100644
--- a/hakai_metadata_conversion/xml.py
+++ b/hakai_metadata_conversion/xml.py
@@ -3,5 +3,3 @@
def xml(record):
return metadata_to_xml(record)
-
-
diff --git a/poetry.lock b/poetry.lock
index 37b99e1..28e2896 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -242,6 +242,20 @@ files = [
{file = "docopt-0.6.2.tar.gz", hash = "sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491"},
]
+[[package]]
+name = "execnet"
+version = "2.1.1"
+description = "execnet: rapid multi-Python deployment"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "execnet-2.1.1-py3-none-any.whl", hash = "sha256:26dee51f1b80cebd6d0ca8e74dd8745419761d3bef34163928cbebbdc4749fdc"},
+ {file = "execnet-2.1.1.tar.gz", hash = "sha256:5189b52c6121c24feae288166ab41b32549c7e2348652736540b9e6e7d4e72e3"},
+]
+
+[package.extras]
+testing = ["hatch", "pre-commit", "pytest", "tox"]
+
[[package]]
name = "idna"
version = "3.7"
@@ -334,6 +348,164 @@ win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""}
[package.extras]
dev = ["Sphinx (==7.2.5)", "colorama (==0.4.5)", "colorama (==0.4.6)", "exceptiongroup (==1.1.3)", "freezegun (==1.1.0)", "freezegun (==1.2.2)", "mypy (==v0.910)", "mypy (==v0.971)", "mypy (==v1.4.1)", "mypy (==v1.5.1)", "pre-commit (==3.4.0)", "pytest (==6.1.2)", "pytest (==7.4.0)", "pytest-cov (==2.12.1)", "pytest-cov (==4.1.0)", "pytest-mypy-plugins (==1.9.3)", "pytest-mypy-plugins (==3.0.0)", "sphinx-autobuild (==2021.3.14)", "sphinx-rtd-theme (==1.3.0)", "tox (==3.27.1)", "tox (==4.11.0)"]
+[[package]]
+name = "lxml"
+version = "5.2.2"
+description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API."
+optional = false
+python-versions = ">=3.6"
+files = [
+ {file = "lxml-5.2.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:364d03207f3e603922d0d3932ef363d55bbf48e3647395765f9bfcbdf6d23632"},
+ {file = "lxml-5.2.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:50127c186f191b8917ea2fb8b206fbebe87fd414a6084d15568c27d0a21d60db"},
+ {file = "lxml-5.2.2-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:74e4f025ef3db1c6da4460dd27c118d8cd136d0391da4e387a15e48e5c975147"},
+ {file = "lxml-5.2.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:981a06a3076997adf7c743dcd0d7a0415582661e2517c7d961493572e909aa1d"},
+ {file = "lxml-5.2.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aef5474d913d3b05e613906ba4090433c515e13ea49c837aca18bde190853dff"},
+ {file = "lxml-5.2.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1e275ea572389e41e8b039ac076a46cb87ee6b8542df3fff26f5baab43713bca"},
+ {file = "lxml-5.2.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5b65529bb2f21ac7861a0e94fdbf5dc0daab41497d18223b46ee8515e5ad297"},
+ {file = "lxml-5.2.2-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:bcc98f911f10278d1daf14b87d65325851a1d29153caaf146877ec37031d5f36"},
+ {file = "lxml-5.2.2-cp310-cp310-manylinux_2_28_ppc64le.whl", hash = "sha256:b47633251727c8fe279f34025844b3b3a3e40cd1b198356d003aa146258d13a2"},
+ {file = "lxml-5.2.2-cp310-cp310-manylinux_2_28_s390x.whl", hash = "sha256:fbc9d316552f9ef7bba39f4edfad4a734d3d6f93341232a9dddadec4f15d425f"},
+ {file = "lxml-5.2.2-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:13e69be35391ce72712184f69000cda04fc89689429179bc4c0ae5f0b7a8c21b"},
+ {file = "lxml-5.2.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3b6a30a9ab040b3f545b697cb3adbf3696c05a3a68aad172e3fd7ca73ab3c835"},
+ {file = "lxml-5.2.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:a233bb68625a85126ac9f1fc66d24337d6e8a0f9207b688eec2e7c880f012ec0"},
+ {file = "lxml-5.2.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:dfa7c241073d8f2b8e8dbc7803c434f57dbb83ae2a3d7892dd068d99e96efe2c"},
+ {file = "lxml-5.2.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1a7aca7964ac4bb07680d5c9d63b9d7028cace3e2d43175cb50bba8c5ad33316"},
+ {file = "lxml-5.2.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ae4073a60ab98529ab8a72ebf429f2a8cc612619a8c04e08bed27450d52103c0"},
+ {file = "lxml-5.2.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:ffb2be176fed4457e445fe540617f0252a72a8bc56208fd65a690fdb1f57660b"},
+ {file = "lxml-5.2.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e290d79a4107d7d794634ce3e985b9ae4f920380a813717adf61804904dc4393"},
+ {file = "lxml-5.2.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:96e85aa09274955bb6bd483eaf5b12abadade01010478154b0ec70284c1b1526"},
+ {file = "lxml-5.2.2-cp310-cp310-win32.whl", hash = "sha256:f956196ef61369f1685d14dad80611488d8dc1ef00be57c0c5a03064005b0f30"},
+ {file = "lxml-5.2.2-cp310-cp310-win_amd64.whl", hash = "sha256:875a3f90d7eb5c5d77e529080d95140eacb3c6d13ad5b616ee8095447b1d22e7"},
+ {file = "lxml-5.2.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:45f9494613160d0405682f9eee781c7e6d1bf45f819654eb249f8f46a2c22545"},
+ {file = "lxml-5.2.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b0b3f2df149efb242cee2ffdeb6674b7f30d23c9a7af26595099afaf46ef4e88"},
+ {file = "lxml-5.2.2-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d28cb356f119a437cc58a13f8135ab8a4c8ece18159eb9194b0d269ec4e28083"},
+ {file = "lxml-5.2.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:657a972f46bbefdbba2d4f14413c0d079f9ae243bd68193cb5061b9732fa54c1"},
+ {file = "lxml-5.2.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b74b9ea10063efb77a965a8d5f4182806fbf59ed068b3c3fd6f30d2ac7bee734"},
+ {file = "lxml-5.2.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:07542787f86112d46d07d4f3c4e7c760282011b354d012dc4141cc12a68cef5f"},
+ {file = "lxml-5.2.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:303f540ad2dddd35b92415b74b900c749ec2010e703ab3bfd6660979d01fd4ed"},
+ {file = "lxml-5.2.2-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:2eb2227ce1ff998faf0cd7fe85bbf086aa41dfc5af3b1d80867ecfe75fb68df3"},
+ {file = "lxml-5.2.2-cp311-cp311-manylinux_2_28_ppc64le.whl", hash = "sha256:1d8a701774dfc42a2f0b8ccdfe7dbc140500d1049e0632a611985d943fcf12df"},
+ {file = "lxml-5.2.2-cp311-cp311-manylinux_2_28_s390x.whl", hash = "sha256:56793b7a1a091a7c286b5f4aa1fe4ae5d1446fe742d00cdf2ffb1077865db10d"},
+ {file = "lxml-5.2.2-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:eb00b549b13bd6d884c863554566095bf6fa9c3cecb2e7b399c4bc7904cb33b5"},
+ {file = "lxml-5.2.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1a2569a1f15ae6c8c64108a2cd2b4a858fc1e13d25846be0666fc144715e32ab"},
+ {file = "lxml-5.2.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:8cf85a6e40ff1f37fe0f25719aadf443686b1ac7652593dc53c7ef9b8492b115"},
+ {file = "lxml-5.2.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:d237ba6664b8e60fd90b8549a149a74fcc675272e0e95539a00522e4ca688b04"},
+ {file = "lxml-5.2.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0b3f5016e00ae7630a4b83d0868fca1e3d494c78a75b1c7252606a3a1c5fc2ad"},
+ {file = "lxml-5.2.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:23441e2b5339bc54dc949e9e675fa35efe858108404ef9aa92f0456929ef6fe8"},
+ {file = "lxml-5.2.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:2fb0ba3e8566548d6c8e7dd82a8229ff47bd8fb8c2da237607ac8e5a1b8312e5"},
+ {file = "lxml-5.2.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:79d1fb9252e7e2cfe4de6e9a6610c7cbb99b9708e2c3e29057f487de5a9eaefa"},
+ {file = "lxml-5.2.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6dcc3d17eac1df7859ae01202e9bb11ffa8c98949dcbeb1069c8b9a75917e01b"},
+ {file = "lxml-5.2.2-cp311-cp311-win32.whl", hash = "sha256:4c30a2f83677876465f44c018830f608fa3c6a8a466eb223535035fbc16f3438"},
+ {file = "lxml-5.2.2-cp311-cp311-win_amd64.whl", hash = "sha256:49095a38eb333aaf44c06052fd2ec3b8f23e19747ca7ec6f6c954ffea6dbf7be"},
+ {file = "lxml-5.2.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:7429e7faa1a60cad26ae4227f4dd0459efde239e494c7312624ce228e04f6391"},
+ {file = "lxml-5.2.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:50ccb5d355961c0f12f6cf24b7187dbabd5433f29e15147a67995474f27d1776"},
+ {file = "lxml-5.2.2-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc911208b18842a3a57266d8e51fc3cfaccee90a5351b92079beed912a7914c2"},
+ {file = "lxml-5.2.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:33ce9e786753743159799fdf8e92a5da351158c4bfb6f2db0bf31e7892a1feb5"},
+ {file = "lxml-5.2.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ec87c44f619380878bd49ca109669c9f221d9ae6883a5bcb3616785fa8f94c97"},
+ {file = "lxml-5.2.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08ea0f606808354eb8f2dfaac095963cb25d9d28e27edcc375d7b30ab01abbf6"},
+ {file = "lxml-5.2.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75a9632f1d4f698b2e6e2e1ada40e71f369b15d69baddb8968dcc8e683839b18"},
+ {file = "lxml-5.2.2-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:74da9f97daec6928567b48c90ea2c82a106b2d500f397eeb8941e47d30b1ca85"},
+ {file = "lxml-5.2.2-cp312-cp312-manylinux_2_28_ppc64le.whl", hash = "sha256:0969e92af09c5687d769731e3f39ed62427cc72176cebb54b7a9d52cc4fa3b73"},
+ {file = "lxml-5.2.2-cp312-cp312-manylinux_2_28_s390x.whl", hash = "sha256:9164361769b6ca7769079f4d426a41df6164879f7f3568be9086e15baca61466"},
+ {file = "lxml-5.2.2-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:d26a618ae1766279f2660aca0081b2220aca6bd1aa06b2cf73f07383faf48927"},
+ {file = "lxml-5.2.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab67ed772c584b7ef2379797bf14b82df9aa5f7438c5b9a09624dd834c1c1aaf"},
+ {file = "lxml-5.2.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:3d1e35572a56941b32c239774d7e9ad724074d37f90c7a7d499ab98761bd80cf"},
+ {file = "lxml-5.2.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:8268cbcd48c5375f46e000adb1390572c98879eb4f77910c6053d25cc3ac2c67"},
+ {file = "lxml-5.2.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e282aedd63c639c07c3857097fc0e236f984ceb4089a8b284da1c526491e3f3d"},
+ {file = "lxml-5.2.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6dfdc2bfe69e9adf0df4915949c22a25b39d175d599bf98e7ddf620a13678585"},
+ {file = "lxml-5.2.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4aefd911793b5d2d7a921233a54c90329bf3d4a6817dc465f12ffdfe4fc7b8fe"},
+ {file = "lxml-5.2.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:8b8df03a9e995b6211dafa63b32f9d405881518ff1ddd775db4e7b98fb545e1c"},
+ {file = "lxml-5.2.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f11ae142f3a322d44513de1018b50f474f8f736bc3cd91d969f464b5bfef8836"},
+ {file = "lxml-5.2.2-cp312-cp312-win32.whl", hash = "sha256:16a8326e51fcdffc886294c1e70b11ddccec836516a343f9ed0f82aac043c24a"},
+ {file = "lxml-5.2.2-cp312-cp312-win_amd64.whl", hash = "sha256:bbc4b80af581e18568ff07f6395c02114d05f4865c2812a1f02f2eaecf0bfd48"},
+ {file = "lxml-5.2.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:e3d9d13603410b72787579769469af730c38f2f25505573a5888a94b62b920f8"},
+ {file = "lxml-5.2.2-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:38b67afb0a06b8575948641c1d6d68e41b83a3abeae2ca9eed2ac59892b36706"},
+ {file = "lxml-5.2.2-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c689d0d5381f56de7bd6966a4541bff6e08bf8d3871bbd89a0c6ab18aa699573"},
+ {file = "lxml-5.2.2-cp36-cp36m-manylinux_2_28_x86_64.whl", hash = "sha256:cf2a978c795b54c539f47964ec05e35c05bd045db5ca1e8366988c7f2fe6b3ce"},
+ {file = "lxml-5.2.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:739e36ef7412b2bd940f75b278749106e6d025e40027c0b94a17ef7968d55d56"},
+ {file = "lxml-5.2.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:d8bbcd21769594dbba9c37d3c819e2d5847656ca99c747ddb31ac1701d0c0ed9"},
+ {file = "lxml-5.2.2-cp36-cp36m-musllinux_1_2_x86_64.whl", hash = "sha256:2304d3c93f2258ccf2cf7a6ba8c761d76ef84948d87bf9664e14d203da2cd264"},
+ {file = "lxml-5.2.2-cp36-cp36m-win32.whl", hash = "sha256:02437fb7308386867c8b7b0e5bc4cd4b04548b1c5d089ffb8e7b31009b961dc3"},
+ {file = "lxml-5.2.2-cp36-cp36m-win_amd64.whl", hash = "sha256:edcfa83e03370032a489430215c1e7783128808fd3e2e0a3225deee278585196"},
+ {file = "lxml-5.2.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:28bf95177400066596cdbcfc933312493799382879da504633d16cf60bba735b"},
+ {file = "lxml-5.2.2-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3a745cc98d504d5bd2c19b10c79c61c7c3df9222629f1b6210c0368177589fb8"},
+ {file = "lxml-5.2.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b590b39ef90c6b22ec0be925b211298e810b4856909c8ca60d27ffbca6c12e6"},
+ {file = "lxml-5.2.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b336b0416828022bfd5a2e3083e7f5ba54b96242159f83c7e3eebaec752f1716"},
+ {file = "lxml-5.2.2-cp37-cp37m-manylinux_2_28_aarch64.whl", hash = "sha256:c2faf60c583af0d135e853c86ac2735ce178f0e338a3c7f9ae8f622fd2eb788c"},
+ {file = "lxml-5.2.2-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:4bc6cb140a7a0ad1f7bc37e018d0ed690b7b6520ade518285dc3171f7a117905"},
+ {file = "lxml-5.2.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7ff762670cada8e05b32bf1e4dc50b140790909caa8303cfddc4d702b71ea184"},
+ {file = "lxml-5.2.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:57f0a0bbc9868e10ebe874e9f129d2917750adf008fe7b9c1598c0fbbfdde6a6"},
+ {file = "lxml-5.2.2-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:a6d2092797b388342c1bc932077ad232f914351932353e2e8706851c870bca1f"},
+ {file = "lxml-5.2.2-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:60499fe961b21264e17a471ec296dcbf4365fbea611bf9e303ab69db7159ce61"},
+ {file = "lxml-5.2.2-cp37-cp37m-win32.whl", hash = "sha256:d9b342c76003c6b9336a80efcc766748a333573abf9350f4094ee46b006ec18f"},
+ {file = "lxml-5.2.2-cp37-cp37m-win_amd64.whl", hash = "sha256:b16db2770517b8799c79aa80f4053cd6f8b716f21f8aca962725a9565ce3ee40"},
+ {file = "lxml-5.2.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7ed07b3062b055d7a7f9d6557a251cc655eed0b3152b76de619516621c56f5d3"},
+ {file = "lxml-5.2.2-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f60fdd125d85bf9c279ffb8e94c78c51b3b6a37711464e1f5f31078b45002421"},
+ {file = "lxml-5.2.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a7e24cb69ee5f32e003f50e016d5fde438010c1022c96738b04fc2423e61706"},
+ {file = "lxml-5.2.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23cfafd56887eaed93d07bc4547abd5e09d837a002b791e9767765492a75883f"},
+ {file = "lxml-5.2.2-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:19b4e485cd07b7d83e3fe3b72132e7df70bfac22b14fe4bf7a23822c3a35bff5"},
+ {file = "lxml-5.2.2-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:7ce7ad8abebe737ad6143d9d3bf94b88b93365ea30a5b81f6877ec9c0dee0a48"},
+ {file = "lxml-5.2.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e49b052b768bb74f58c7dda4e0bdf7b79d43a9204ca584ffe1fb48a6f3c84c66"},
+ {file = "lxml-5.2.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d14a0d029a4e176795cef99c056d58067c06195e0c7e2dbb293bf95c08f772a3"},
+ {file = "lxml-5.2.2-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:be49ad33819d7dcc28a309b86d4ed98e1a65f3075c6acd3cd4fe32103235222b"},
+ {file = "lxml-5.2.2-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:a6d17e0370d2516d5bb9062c7b4cb731cff921fc875644c3d751ad857ba9c5b1"},
+ {file = "lxml-5.2.2-cp38-cp38-win32.whl", hash = "sha256:5b8c041b6265e08eac8a724b74b655404070b636a8dd6d7a13c3adc07882ef30"},
+ {file = "lxml-5.2.2-cp38-cp38-win_amd64.whl", hash = "sha256:f61efaf4bed1cc0860e567d2ecb2363974d414f7f1f124b1df368bbf183453a6"},
+ {file = "lxml-5.2.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:fb91819461b1b56d06fa4bcf86617fac795f6a99d12239fb0c68dbeba41a0a30"},
+ {file = "lxml-5.2.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d4ed0c7cbecde7194cd3228c044e86bf73e30a23505af852857c09c24e77ec5d"},
+ {file = "lxml-5.2.2-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54401c77a63cc7d6dc4b4e173bb484f28a5607f3df71484709fe037c92d4f0ed"},
+ {file = "lxml-5.2.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:625e3ef310e7fa3a761d48ca7ea1f9d8718a32b1542e727d584d82f4453d5eeb"},
+ {file = "lxml-5.2.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:519895c99c815a1a24a926d5b60627ce5ea48e9f639a5cd328bda0515ea0f10c"},
+ {file = "lxml-5.2.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c7079d5eb1c1315a858bbf180000757db8ad904a89476653232db835c3114001"},
+ {file = "lxml-5.2.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:343ab62e9ca78094f2306aefed67dcfad61c4683f87eee48ff2fd74902447726"},
+ {file = "lxml-5.2.2-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:cd9e78285da6c9ba2d5c769628f43ef66d96ac3085e59b10ad4f3707980710d3"},
+ {file = "lxml-5.2.2-cp39-cp39-manylinux_2_28_ppc64le.whl", hash = "sha256:546cf886f6242dff9ec206331209db9c8e1643ae642dea5fdbecae2453cb50fd"},
+ {file = "lxml-5.2.2-cp39-cp39-manylinux_2_28_s390x.whl", hash = "sha256:02f6a8eb6512fdc2fd4ca10a49c341c4e109aa6e9448cc4859af5b949622715a"},
+ {file = "lxml-5.2.2-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:339ee4a4704bc724757cd5dd9dc8cf4d00980f5d3e6e06d5847c1b594ace68ab"},
+ {file = "lxml-5.2.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0a028b61a2e357ace98b1615fc03f76eb517cc028993964fe08ad514b1e8892d"},
+ {file = "lxml-5.2.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:f90e552ecbad426eab352e7b2933091f2be77115bb16f09f78404861c8322981"},
+ {file = "lxml-5.2.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:d83e2d94b69bf31ead2fa45f0acdef0757fa0458a129734f59f67f3d2eb7ef32"},
+ {file = "lxml-5.2.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a02d3c48f9bb1e10c7788d92c0c7db6f2002d024ab6e74d6f45ae33e3d0288a3"},
+ {file = "lxml-5.2.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:6d68ce8e7b2075390e8ac1e1d3a99e8b6372c694bbe612632606d1d546794207"},
+ {file = "lxml-5.2.2-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:453d037e09a5176d92ec0fd282e934ed26d806331a8b70ab431a81e2fbabf56d"},
+ {file = "lxml-5.2.2-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:3b019d4ee84b683342af793b56bb35034bd749e4cbdd3d33f7d1107790f8c472"},
+ {file = "lxml-5.2.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:cb3942960f0beb9f46e2a71a3aca220d1ca32feb5a398656be934320804c0df9"},
+ {file = "lxml-5.2.2-cp39-cp39-win32.whl", hash = "sha256:ac6540c9fff6e3813d29d0403ee7a81897f1d8ecc09a8ff84d2eea70ede1cdbf"},
+ {file = "lxml-5.2.2-cp39-cp39-win_amd64.whl", hash = "sha256:610b5c77428a50269f38a534057444c249976433f40f53e3b47e68349cca1425"},
+ {file = "lxml-5.2.2-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:b537bd04d7ccd7c6350cdaaaad911f6312cbd61e6e6045542f781c7f8b2e99d2"},
+ {file = "lxml-5.2.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4820c02195d6dfb7b8508ff276752f6b2ff8b64ae5d13ebe02e7667e035000b9"},
+ {file = "lxml-5.2.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2a09f6184f17a80897172863a655467da2b11151ec98ba8d7af89f17bf63dae"},
+ {file = "lxml-5.2.2-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:76acba4c66c47d27c8365e7c10b3d8016a7da83d3191d053a58382311a8bf4e1"},
+ {file = "lxml-5.2.2-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:b128092c927eaf485928cec0c28f6b8bead277e28acf56800e972aa2c2abd7a2"},
+ {file = "lxml-5.2.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ae791f6bd43305aade8c0e22f816b34f3b72b6c820477aab4d18473a37e8090b"},
+ {file = "lxml-5.2.2-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a2f6a1bc2460e643785a2cde17293bd7a8f990884b822f7bca47bee0a82fc66b"},
+ {file = "lxml-5.2.2-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e8d351ff44c1638cb6e980623d517abd9f580d2e53bfcd18d8941c052a5a009"},
+ {file = "lxml-5.2.2-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bec4bd9133420c5c52d562469c754f27c5c9e36ee06abc169612c959bd7dbb07"},
+ {file = "lxml-5.2.2-pp37-pypy37_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:55ce6b6d803890bd3cc89975fca9de1dff39729b43b73cb15ddd933b8bc20484"},
+ {file = "lxml-5.2.2-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:8ab6a358d1286498d80fe67bd3d69fcbc7d1359b45b41e74c4a26964ca99c3f8"},
+ {file = "lxml-5.2.2-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:06668e39e1f3c065349c51ac27ae430719d7806c026fec462e5693b08b95696b"},
+ {file = "lxml-5.2.2-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9cd5323344d8ebb9fb5e96da5de5ad4ebab993bbf51674259dbe9d7a18049525"},
+ {file = "lxml-5.2.2-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89feb82ca055af0fe797a2323ec9043b26bc371365847dbe83c7fd2e2f181c34"},
+ {file = "lxml-5.2.2-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e481bba1e11ba585fb06db666bfc23dbe181dbafc7b25776156120bf12e0d5a6"},
+ {file = "lxml-5.2.2-pp38-pypy38_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:9d6c6ea6a11ca0ff9cd0390b885984ed31157c168565702959c25e2191674a14"},
+ {file = "lxml-5.2.2-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:3d98de734abee23e61f6b8c2e08a88453ada7d6486dc7cdc82922a03968928db"},
+ {file = "lxml-5.2.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:69ab77a1373f1e7563e0fb5a29a8440367dec051da6c7405333699d07444f511"},
+ {file = "lxml-5.2.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:34e17913c431f5ae01d8658dbf792fdc457073dcdfbb31dc0cc6ab256e664a8d"},
+ {file = "lxml-5.2.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05f8757b03208c3f50097761be2dea0aba02e94f0dc7023ed73a7bb14ff11eb0"},
+ {file = "lxml-5.2.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6a520b4f9974b0a0a6ed73c2154de57cdfd0c8800f4f15ab2b73238ffed0b36e"},
+ {file = "lxml-5.2.2-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:5e097646944b66207023bc3c634827de858aebc226d5d4d6d16f0b77566ea182"},
+ {file = "lxml-5.2.2-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:b5e4ef22ff25bfd4ede5f8fb30f7b24446345f3e79d9b7455aef2836437bc38a"},
+ {file = "lxml-5.2.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:ff69a9a0b4b17d78170c73abe2ab12084bdf1691550c5629ad1fe7849433f324"},
+ {file = "lxml-5.2.2.tar.gz", hash = "sha256:bb2dc4898180bea79863d5487e5f9c7c34297414bad54bcd0f0852aee9cfdb87"},
+]
+
+[package.extras]
+cssselect = ["cssselect (>=0.7)"]
+html-clean = ["lxml-html-clean"]
+html5 = ["html5lib"]
+htmlsoup = ["BeautifulSoup4"]
+source = ["Cython (>=3.0.10)"]
+
[[package]]
name = "markupsafe"
version = "2.1.5"
@@ -577,6 +749,26 @@ pluggy = ">=1.5,<2.0"
[package.extras]
dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"]
+[[package]]
+name = "pytest-xdist"
+version = "3.6.1"
+description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "pytest_xdist-3.6.1-py3-none-any.whl", hash = "sha256:9ed4adfb68a016610848639bb7e02c9352d5d9f03d04809919e2dafc3be4cca7"},
+ {file = "pytest_xdist-3.6.1.tar.gz", hash = "sha256:ead156a4db231eec769737f57668ef58a2084a34b2e55c4a8fa20d861107300d"},
+]
+
+[package.dependencies]
+execnet = ">=2.1"
+pytest = ">=7.0.0"
+
+[package.extras]
+psutil = ["psutil (>=3.0)"]
+setproctitle = ["setproctitle"]
+testing = ["filelock"]
+
[[package]]
name = "python-dateutil"
version = "2.9.0.post0"
diff --git a/pyproject.toml b/pyproject.toml
index b1b8f9c..11f6ddc 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -14,6 +14,7 @@ cffconvert = "^2.0.0"
pycountry = "^24.6.1"
metadata-xml = {git = "https://github.com/hakaiinstitute/metadata-xml.git"}
pyyaml = "^6.0.1"
+lxml = "^5.2.2"
[tool.poetry.group.dev.dependencies]
@@ -21,6 +22,7 @@ pytest = "^8.2.2"
isort = "^5.13.2"
ruff = "^0.5.0"
black = "^24.4.2"
+pytest-xdist = "^3.6.1"
[tool.poetry.group.docs.dependencies]
@@ -31,4 +33,4 @@ requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
[tool.poetry.scripts]
-hakai_metadata_conversion = "hakai_metadata_conversion.__main__:cli_main"
+hakai_metadata_conversion = "hakai_metadata_conversion.__main__:cli"
\ No newline at end of file
diff --git a/test_erddap_update_dataset.xml b/test_erddap_update_dataset.xml
new file mode 100644
index 0000000..c06e3a2
--- /dev/null
+++ b/test_erddap_update_dataset.xml
@@ -0,0 +1,813 @@
+
+
+ hakai_erddap_sourceUrl
+ org.postgresql.Driver
+ erddap
+ "TestDataset1"
+
+ false
+ https://catalogue.hakai.orgca-cioos_ba41d935-f293-447f-be3d-7098e569b431
+ CF Standard Name Table v79
+ station
+ TimeSeries
+ station
+ coastal structure
+ http://vocab.nerc.ac.uk/collection/L06/current/
+ Oceanography,Nearshore,Watersheds
+ CC-BY-4.0
+ Dakunalytics, LLC - Burke-o-Lator pCO2/TCO2 analyzer - ,Seabird Electronics - SBE 45 -
+ ba41d935-f293-447f-be3d-7098e569b431
+ ca.cioos
+ fluorescence,turbidity,PAR,photosynthetically active radiation,Calvert Island,Quadra Island,transmissometer,Johnstone Strait,Queen Charlotte Sound,Strait of Georgia,salinity,coastal zone,CIOOS:oxygen,CIOOS:subSurfaceSalinity,CIOOS:subSurfaceTemperature
+ land/onshore structure
+ http://vocab.nerc.ac.uk/collection/L06/current/
+ Temperature, conductivity, dissolved oxygen, fluorescence, photosynthetic active radiation, and turbidity data collected from 2012 to present by the Hakai Institute in waters surrounding Calvert Island, Johnstone Strait, and Quadra Island areas. This dataset presents data collected by oceanographic profiler instruments (RBR XR-620, RBR Concerto, RBR Maestro, and Seabird SBE 19plus v2) which have been automatically processed by following respective manufacturer's guidelines (see Hakai Water Properties Profile Processing and QA/QC Procedure Manual).
+
+The provisional processed data are then quality controlled by applying a series of tests that are following the QARTOD standards and more tests specific to the Hakai Institute data (see Hakai Water Properties Profile Processing and QA/QC Procedure Manual).
+
+The research dataset provides a subset of the provisional dataset which has been manually reviewed and judged good for science quality level.
+
+Data were collected by the Hakai Institute Oceanography Program, the Nearshore Program, and the Juvenile Salmon Program.
+ Hakai Water Properties Vertical Profile Data Measured by Oceanographic Profilers, Research
+ ##Limitations:
+Instruments sensors are generally calibrated once a year by the manufacturer, no comparison and post-calibration correction based on data sampled by another method is applied to this dataset.onGoingCIOOS: CIOOS Essential Ocean Variables Vocabulary2024-03-18T19:28:21.285Z2024-03-191.0.0These data have been solely funded by the Tula Foundation. Data collection started near Calvert Island in 2012, near Quadra Island in 2014, and in Johnstone Strait from 2015 to 2019.Hakai Institutedata@hakai.orginstitutionHakai Institute1713 Hyacinthe Bay RoadHeriot BayCanadahakai.orgHakai Institutedata@hakai.orginstitutionHakai Institute1713 Hyacinthe Bay RoadHeriot BayCanadahakai.orgHakai Institute;lastname, firstname;asd, as;Tula Foundationdistributor,owner,funder,publisher;custodian,pointOfContact,processor;processor;funderhttps://doi.org/10.21966/kace-2d24https://catalogue.hakai.orgca-cioos_ba41d935-f293-447f-be3d-7098e569b431https://hakaiinstitute.github.io/hakai-metadata-entry-form#/en/hakai/tV5qE0aUgaOjSVmgPgiZ6MyHuSy1/-MX35qgX-BrwJDdeJKe1
+
+
+ measurementTime
+ time
+ String
+
+ time
+ seconds since 1970-01-01T00:00:00Z
+ time: point (interval: 5.0 minutes comment: time correspond to the end of the interval)
+
+
+
+
+
+ =48.836633
+ latitude
+ double
+
+ Fixed latitude of the site
+ Latitude
+ latitude
+ degrees_north
+
+
+
+
+
+ =-125.1362667
+ longitude
+ double
+
+ Fixed longitude of the site
+ Longitude
+ longitude
+ degrees_east
+
+
+
+
+
+ =20
+ depth
+ double
+
+ Approximative depth
+ Depth
+ depth
+ m
+
+
+
+
+ ="Nova Harvest Fisheries Burke-o-Lator"
+ station
+ String
+
+ Location
+ timeseries_id
+
+
+
+
+
+ BamfieldBoL:pCO2_uatm_Med
+ pCO2_uatm_Med
+ double
+
+ partial_pressure_of_carbon_dioxide_in_sea_water
+ BoL
+ Burke-o-Lator
+ pCO2_Med
+ μatm
+ time: median (interval: 5.0 minutes)
+ pCO2_uatm_UQL pCO2_uatm_flags
+
+
+
+
+
+ BamfieldBoL:pCO2_uatm_Avg
+ pCO2_uatm_Avg
+ double
+
+ partial_pressure_of_carbon_dioxide_in_sea_water
+ BoL
+ Burke-o-Lator
+ pCO2_Avg
+ μatm
+ time: mean (interval: 5.0 minutes)
+ pCO2_uatm_UQL pCO2_uatm_flags
+
+
+
+
+
+ BamfieldBoL:pCO2_uatm_Min
+ pCO2_uatm_Min
+ double
+
+ partial_pressure_of_carbon_dioxide_in_sea_water
+ BoL
+ Burke-o-Lator
+ pCO2_Min
+ μatm
+ time: mininum (interval: 5.0 minutes)
+ pCO2_uatm_UQL pCO2_uatm_flags
+
+
+
+
+
+ BamfieldBoL:pCO2_uatm_Max
+ pCO2_uatm_Max
+ double
+
+ partial_pressure_of_carbon_dioxide_in_sea_water
+ BoL
+ Burke-o-Lator
+ pCO2_Max
+ μatm
+ time: maximum (interval: 5.0 minutes)
+ pCO2_uatm_UQL pCO2_uatm_flags
+
+
+
+
+
+ BamfieldBoL:pCO2_uatm_Std
+ pCO2_uatm_Std
+ double
+
+ partial_pressure_of_carbon_dioxide_in_sea_water
+ BoL
+ Burke-o-Lator
+ pCO2_Std
+ μatm
+ time: standard_deviation (interval: 5.0 minutes)
+ pCO2_uatm_UQL pCO2_uatm_flags
+
+
+
+
+
+ BamfieldBoL:pCO2_uatm_UQL
+ pCO2_uatm_UQL
+ byte
+
+ pCO2 UNESCO Q level
+ PASS NOT_EVALUATED SUSPECT FAIL MISSING
+ 1 2 3 4 9
+ 1
+ aggregate_quality_flag
+ 2
+
+
+
+
+
+ BamfieldBoL:pCO2_uatm_QC
+ pCO2_uatm_flags
+ String
+
+ pCO2 Q flags
+ Quality flags
+
+
+
+
+
+ BamfieldBoL:calcTCO2_Med
+ calcTCO2_Med
+ double
+
+ BoL
+ Burke-o-Lator
+ TCO2_Med
+ μmol/kg
+ time: median (interval: 5.0 minutes)
+ calcTCO2_UQL calcTCO2_flags
+
+
+
+
+
+ BamfieldBoL:calcTCO2_Avg
+ calcTCO2_Avg
+ double
+
+ BoL
+ Burke-o-Lator
+ TCO2_Avg
+ μmol/kg
+ time: mean (interval: 5.0 minutes)
+ calcTCO2_UQL calcTCO2_flags
+
+
+
+
+
+ BamfieldBoL:calcTCO2_Min
+ calcTCO2_Min
+ double
+
+ BoL
+ Burke-o-Lator
+ TCO2_Min
+ μmol/kg
+ time: mininum (interval: 5.0 minutes)
+ calcTCO2_UQL calcTCO2_flags
+
+
+
+
+
+ BamfieldBoL:calcTCO2_Max
+ calcTCO2_Max
+ double
+
+ BoL
+ Burke-o-Lator
+ TCO2_Max
+ μmol/kg
+ time: maximum (interval: 5.0 minutes)
+ calcTCO2_UQL calcTCO2_flags
+
+
+
+
+
+ BamfieldBoL:calcTCO2_Std
+ calcTCO2_Std
+ double
+
+ BoL
+ Burke-o-Lator
+ TCO2_Std
+ μmol/kg
+ time: standard_deviation (interval: 5.0 minutes)
+ calcTCO2_UQL calcTCO2_flags
+
+
+
+
+
+ BamfieldBoL:calcTCO2_UQL
+ calcTCO2_UQL
+ byte
+
+ TCO2 UNESCO Q level
+ PASS NOT_EVALUATED SUSPECT FAIL MISSING
+ 1 2 3 4 9
+ 1
+ aggregate_quality_flag
+ 2
+
+
+
+
+
+ BamfieldBoL:calcTCO2_QC
+ calcTCO2_flags
+ String
+
+ TCO2 Q flags
+ Quality flags
+
+
+
+
+
+ BamfieldBoL:TSG_T_Med
+ TSG_T_Med
+ double
+
+ sea_water_temperature
+ BoL
+ Burke-o-Lator
+ Water temperature_Med
+ degree_Celsius
+ time: median (interval: 5.0 minutes)
+ TSG_T_UQL TSG_T_flags
+
+
+
+
+
+ BamfieldBoL:TSG_T_Avg
+ TSG_T_Avg
+ double
+
+ sea_water_temperature
+ BoL
+ Burke-o-Lator
+ Water temperature_Avg
+ degree_Celsius
+ time: mean (interval: 5.0 minutes)
+ TSG_T_UQL TSG_T_flags
+
+
+
+
+
+ BamfieldBoL:TSG_T_Min
+ TSG_T_Min
+ double
+
+ sea_water_temperature
+ BoL
+ Burke-o-Lator
+ Water temperature_Min
+ degree_Celsius
+ time: mininum (interval: 5.0 minutes)
+ TSG_T_UQL TSG_T_flags
+
+
+
+
+
+ BamfieldBoL:TSG_T_Max
+ TSG_T_Max
+ double
+
+ sea_water_temperature
+ BoL
+ Burke-o-Lator
+ Water temperature_Max
+ degree_Celsius
+ time: maximum (interval: 5.0 minutes)
+ TSG_T_UQL TSG_T_flags
+
+
+
+
+
+ BamfieldBoL:TSG_T_Std
+ TSG_T_Std
+ double
+
+ sea_water_temperature
+ BoL
+ Burke-o-Lator
+ Water temperature_Std
+ degree_Celsius
+ time: standard_deviation (interval: 5.0 minutes)
+ TSG_T_UQL TSG_T_flags
+
+
+
+
+
+ BamfieldBoL:TSG_T_UQL
+ TSG_T_UQL
+ byte
+
+ Water temperature UNESCO Q level
+ PASS NOT_EVALUATED SUSPECT FAIL MISSING
+ 1 2 3 4 9
+ 1
+ aggregate_quality_flag
+ 2
+
+
+
+
+
+ BamfieldBoL:TSG_T_QC
+ TSG_T_flags
+ String
+
+ Water temperature Q flags
+ Quality flags
+
+
+
+
+
+ BamfieldBoL:TSG_S_Med
+ TSG_S_Med
+ double
+
+ sea_water_practical_salinity
+ BoL
+ Burke-o-Lator
+ Salinity_Med
+ time: median (interval: 5.0 minutes)
+ TSG_S_UQL TSG_S_flags
+
+
+
+
+
+ BamfieldBoL:TSG_S_Avg
+ TSG_S_Avg
+ double
+
+ sea_water_practical_salinity
+ BoL
+ Burke-o-Lator
+ Salinity_Avg
+ time: mean (interval: 5.0 minutes)
+ TSG_S_UQL TSG_S_flags
+
+
+
+
+
+ BamfieldBoL:TSG_S_Min
+ TSG_S_Min
+ double
+
+ sea_water_practical_salinity
+ BoL
+ Burke-o-Lator
+ Salinity_Min
+ time: mininum (interval: 5.0 minutes)
+ TSG_S_UQL TSG_S_flags
+
+
+
+
+
+ BamfieldBoL:TSG_S_Max
+ TSG_S_Max
+ double
+
+ sea_water_practical_salinity
+ BoL
+ Burke-o-Lator
+ Salinity_Max
+ time: maximum (interval: 5.0 minutes)
+ TSG_S_UQL TSG_S_flags
+
+
+
+
+
+ BamfieldBoL:TSG_S_Std
+ TSG_S_Std
+ double
+
+ sea_water_practical_salinity
+ BoL
+ Burke-o-Lator
+ Salinity_Std
+ time: standard_deviation (interval: 5.0 minutes)
+ TSG_S_UQL TSG_S_flags
+
+
+
+
+
+ BamfieldBoL:TSG_S_UQL
+ TSG_S_UQL
+ byte
+
+ Salinity UNESCO Q level
+ PASS NOT_EVALUATED SUSPECT FAIL MISSING
+ 1 2 3 4 9
+ 1
+ aggregate_quality_flag
+ 2
+
+
+
+
+
+ BamfieldBoL:TSG_S_QC
+ TSG_S_flags
+ String
+
+ Salinity Q flags
+ Quality flags
+
+
+
+
+
+ BamfieldBoL:AlkS_Med
+ AlkS_Med
+ double
+
+ BoL
+ Burke-o-Lator
+ TA_Med
+ μmol/kg
+ time: median (interval: 5.0 minutes)
+ AlkS_UQL AlkS_flags
+
+
+
+
+
+ BamfieldBoL:AlkS_Avg
+ AlkS_Avg
+ double
+
+ BoL
+ Burke-o-Lator
+ TA_Avg
+ μmol/kg
+ time: mean (interval: 5.0 minutes)
+ AlkS_UQL AlkS_flags
+
+
+
+
+
+ BamfieldBoL:AlkS_Min
+ AlkS_Min
+ double
+
+ BoL
+ Burke-o-Lator
+ TA_Min
+ μmol/kg
+ time: mininum (interval: 5.0 minutes)
+ AlkS_UQL AlkS_flags
+
+
+
+
+
+ BamfieldBoL:AlkS_Max
+ AlkS_Max
+ double
+
+ BoL
+ Burke-o-Lator
+ TA_Max
+ μmol/kg
+ time: maximum (interval: 5.0 minutes)
+ AlkS_UQL AlkS_flags
+
+
+
+
+
+ BamfieldBoL:AlkS_Std
+ AlkS_Std
+ double
+
+ BoL
+ Burke-o-Lator
+ TA_Std
+ μmol/kg
+ time: standard_deviation (interval: 5.0 minutes)
+ AlkS_UQL AlkS_flags
+
+
+
+
+
+ BamfieldBoL:AlkS_UQL
+ AlkS_UQL
+ byte
+
+ TA UNESCO Q level
+ PASS NOT_EVALUATED SUSPECT FAIL MISSING
+ 1 2 3 4 9
+ 1
+ aggregate_quality_flag
+ 2
+
+
+
+
+
+ BamfieldBoL:AlkS_QC
+ AlkS_flags
+ String
+
+ TA Q flags
+ Quality flags
+
+
+
+
+
+ BamfieldBoL:calcOmega_Med
+ calcOmega_Med
+ double
+
+ BoL
+ Burke-o-Lator
+ Ω arag_Med
+ time: median (interval: 5.0 minutes)
+ calcOmega_UQL calcOmega_flags
+
+
+
+
+
+ BamfieldBoL:calcOmega_Avg
+ calcOmega_Avg
+ double
+
+ BoL
+ Burke-o-Lator
+ Ω arag_Avg
+ time: mean (interval: 5.0 minutes)
+ calcOmega_UQL calcOmega_flags
+
+
+
+
+
+ BamfieldBoL:calcOmega_Min
+ calcOmega_Min
+ double
+
+ BoL
+ Burke-o-Lator
+ Ω arag_Min
+ time: mininum (interval: 5.0 minutes)
+ calcOmega_UQL calcOmega_flags
+
+
+
+
+
+ BamfieldBoL:calcOmega_Max
+ calcOmega_Max
+ double
+
+ BoL
+ Burke-o-Lator
+ Ω arag_Max
+ time: maximum (interval: 5.0 minutes)
+ calcOmega_UQL calcOmega_flags
+
+
+
+
+
+ BamfieldBoL:calcOmega_Std
+ calcOmega_Std
+ double
+
+ BoL
+ Burke-o-Lator
+ Ω arag_Std
+ time: standard_deviation (interval: 5.0 minutes)
+ calcOmega_UQL calcOmega_flags
+
+
+
+
+
+ BamfieldBoL:calcOmega_UQL
+ calcOmega_UQL
+ byte
+
+ Ω arag UNESCO Q level
+ PASS NOT_EVALUATED SUSPECT FAIL MISSING
+ 1 2 3 4 9
+ 1
+ aggregate_quality_flag
+ 2
+
+
+
+
+
+ BamfieldBoL:calcOmega_QC
+ calcOmega_flags
+ String
+
+ Ω arag Q flags
+ Quality flags
+
+
+
+
+
+ BamfieldBoL:calcpH_Med
+ calcpH_Med
+ double
+
+ sea_water_ph_reported_on_total_scale
+ BoL
+ Burke-o-Lator
+ pH_T_Med
+ total scale
+ time: median (interval: 5.0 minutes)
+ pCO2_uacalcpH_UQL tm_UQL calcpH_UQL
+
+
+
+
+
+ BamfieldBoL:calcpH_Avg
+ calcpH_Avg
+ double
+
+ sea_water_ph_reported_on_total_scale
+ BoL
+ Burke-o-Lator
+ pH_T_Avg
+ total scale
+ time: mean (interval: 5.0 minutes)
+ pCO2_uacalcpH_UQL tm_UQL calcpH_UQL
+
+
+
+
+
+ BamfieldBoL:calcpH_Min
+ calcpH_Min
+ double
+
+ sea_water_ph_reported_on_total_scale
+ BoL
+ Burke-o-Lator
+ pH_T_Min
+ total scale
+ time: mininum (interval: 5.0 minutes)
+ pCO2_uacalcpH_UQL tm_UQL calcpH_UQL
+
+
+
+
+
+ BamfieldBoL:calcpH_Max
+ calcpH_Max
+ double
+
+ sea_water_ph_reported_on_total_scale
+ BoL
+ Burke-o-Lator
+ pH_T_Max
+ total scale
+ time: maximum (interval: 5.0 minutes)
+ pCO2_uacalcpH_UQL tm_UQL calcpH_UQL
+
+
+
+
+
+ BamfieldBoL:calcpH_Std
+ calcpH_Std
+ double
+
+ sea_water_ph_reported_on_total_scale
+ BoL
+ Burke-o-Lator
+ pH_T_Std
+ total scale
+ time: standard_deviation (interval: 5.0 minutes)
+ pCO2_uacalcpH_UQL tm_UQL calcpH_UQL
+
+
+
+
+
+ BamfieldBoL:calcpH_UQL
+ calcpH_UQL
+ byte
+
+ pH_T UNESCO Q level
+ PASS NOT_EVALUATED SUSPECT FAIL MISSING
+ 1 2 3 4 9
+ 1
+ aggregate_quality_flag
+ 2
+
+
+
+
+
+ BamfieldBoL:calcpH_QC
+ calcpH_flags
+ String
+
+ pH_T Q flags
+ Quality flags
+
+
+
+
+
diff --git a/test_result.xml b/test_result.xml
new file mode 100644
index 0000000..c06e3a2
--- /dev/null
+++ b/test_result.xml
@@ -0,0 +1,813 @@
+
+
+ hakai_erddap_sourceUrl
+ org.postgresql.Driver
+ erddap
+ "TestDataset1"
+
+ false
+ https://catalogue.hakai.orgca-cioos_ba41d935-f293-447f-be3d-7098e569b431
+ CF Standard Name Table v79
+ station
+ TimeSeries
+ station
+ coastal structure
+ http://vocab.nerc.ac.uk/collection/L06/current/
+ Oceanography,Nearshore,Watersheds
+ CC-BY-4.0
+ Dakunalytics, LLC - Burke-o-Lator pCO2/TCO2 analyzer - ,Seabird Electronics - SBE 45 -
+ ba41d935-f293-447f-be3d-7098e569b431
+ ca.cioos
+ fluorescence,turbidity,PAR,photosynthetically active radiation,Calvert Island,Quadra Island,transmissometer,Johnstone Strait,Queen Charlotte Sound,Strait of Georgia,salinity,coastal zone,CIOOS:oxygen,CIOOS:subSurfaceSalinity,CIOOS:subSurfaceTemperature
+ land/onshore structure
+ http://vocab.nerc.ac.uk/collection/L06/current/
+ Temperature, conductivity, dissolved oxygen, fluorescence, photosynthetic active radiation, and turbidity data collected from 2012 to present by the Hakai Institute in waters surrounding Calvert Island, Johnstone Strait, and Quadra Island areas. This dataset presents data collected by oceanographic profiler instruments (RBR XR-620, RBR Concerto, RBR Maestro, and Seabird SBE 19plus v2) which have been automatically processed by following respective manufacturer's guidelines (see Hakai Water Properties Profile Processing and QA/QC Procedure Manual).
+
+The provisional processed data are then quality controlled by applying a series of tests that are following the QARTOD standards and more tests specific to the Hakai Institute data (see Hakai Water Properties Profile Processing and QA/QC Procedure Manual).
+
+The research dataset provides a subset of the provisional dataset which has been manually reviewed and judged good for science quality level.
+
+Data were collected by the Hakai Institute Oceanography Program, the Nearshore Program, and the Juvenile Salmon Program.
+ Hakai Water Properties Vertical Profile Data Measured by Oceanographic Profilers, Research
+ ##Limitations:
+Instruments sensors are generally calibrated once a year by the manufacturer, no comparison and post-calibration correction based on data sampled by another method is applied to this dataset.onGoingCIOOS: CIOOS Essential Ocean Variables Vocabulary2024-03-18T19:28:21.285Z2024-03-191.0.0These data have been solely funded by the Tula Foundation. Data collection started near Calvert Island in 2012, near Quadra Island in 2014, and in Johnstone Strait from 2015 to 2019.Hakai Institutedata@hakai.orginstitutionHakai Institute1713 Hyacinthe Bay RoadHeriot BayCanadahakai.orgHakai Institutedata@hakai.orginstitutionHakai Institute1713 Hyacinthe Bay RoadHeriot BayCanadahakai.orgHakai Institute;lastname, firstname;asd, as;Tula Foundationdistributor,owner,funder,publisher;custodian,pointOfContact,processor;processor;funderhttps://doi.org/10.21966/kace-2d24https://catalogue.hakai.orgca-cioos_ba41d935-f293-447f-be3d-7098e569b431https://hakaiinstitute.github.io/hakai-metadata-entry-form#/en/hakai/tV5qE0aUgaOjSVmgPgiZ6MyHuSy1/-MX35qgX-BrwJDdeJKe1
+
+
+ measurementTime
+ time
+ String
+
+ time
+ seconds since 1970-01-01T00:00:00Z
+ time: point (interval: 5.0 minutes comment: time correspond to the end of the interval)
+
+
+
+
+
+ =48.836633
+ latitude
+ double
+
+ Fixed latitude of the site
+ Latitude
+ latitude
+ degrees_north
+
+
+
+
+
+ =-125.1362667
+ longitude
+ double
+
+ Fixed longitude of the site
+ Longitude
+ longitude
+ degrees_east
+
+
+
+
+
+ =20
+ depth
+ double
+
+ Approximative depth
+ Depth
+ depth
+ m
+
+
+
+
+ ="Nova Harvest Fisheries Burke-o-Lator"
+ station
+ String
+
+ Location
+ timeseries_id
+
+
+
+
+
+ BamfieldBoL:pCO2_uatm_Med
+ pCO2_uatm_Med
+ double
+
+ partial_pressure_of_carbon_dioxide_in_sea_water
+ BoL
+ Burke-o-Lator
+ pCO2_Med
+ μatm
+ time: median (interval: 5.0 minutes)
+ pCO2_uatm_UQL pCO2_uatm_flags
+
+
+
+
+
+ BamfieldBoL:pCO2_uatm_Avg
+ pCO2_uatm_Avg
+ double
+
+ partial_pressure_of_carbon_dioxide_in_sea_water
+ BoL
+ Burke-o-Lator
+ pCO2_Avg
+ μatm
+ time: mean (interval: 5.0 minutes)
+ pCO2_uatm_UQL pCO2_uatm_flags
+
+
+
+
+
+ BamfieldBoL:pCO2_uatm_Min
+ pCO2_uatm_Min
+ double
+
+ partial_pressure_of_carbon_dioxide_in_sea_water
+ BoL
+ Burke-o-Lator
+ pCO2_Min
+ μatm
+ time: mininum (interval: 5.0 minutes)
+ pCO2_uatm_UQL pCO2_uatm_flags
+
+
+
+
+
+ BamfieldBoL:pCO2_uatm_Max
+ pCO2_uatm_Max
+ double
+
+ partial_pressure_of_carbon_dioxide_in_sea_water
+ BoL
+ Burke-o-Lator
+ pCO2_Max
+ μatm
+ time: maximum (interval: 5.0 minutes)
+ pCO2_uatm_UQL pCO2_uatm_flags
+
+
+
+
+
+ BamfieldBoL:pCO2_uatm_Std
+ pCO2_uatm_Std
+ double
+
+ partial_pressure_of_carbon_dioxide_in_sea_water
+ BoL
+ Burke-o-Lator
+ pCO2_Std
+ μatm
+ time: standard_deviation (interval: 5.0 minutes)
+ pCO2_uatm_UQL pCO2_uatm_flags
+
+
+
+
+
+ BamfieldBoL:pCO2_uatm_UQL
+ pCO2_uatm_UQL
+ byte
+
+ pCO2 UNESCO Q level
+ PASS NOT_EVALUATED SUSPECT FAIL MISSING
+ 1 2 3 4 9
+ 1
+ aggregate_quality_flag
+ 2
+
+
+
+
+
+ BamfieldBoL:pCO2_uatm_QC
+ pCO2_uatm_flags
+ String
+
+ pCO2 Q flags
+ Quality flags
+
+
+
+
+
+ BamfieldBoL:calcTCO2_Med
+ calcTCO2_Med
+ double
+
+ BoL
+ Burke-o-Lator
+ TCO2_Med
+ μmol/kg
+ time: median (interval: 5.0 minutes)
+ calcTCO2_UQL calcTCO2_flags
+
+
+
+
+
+ BamfieldBoL:calcTCO2_Avg
+ calcTCO2_Avg
+ double
+
+ BoL
+ Burke-o-Lator
+ TCO2_Avg
+ μmol/kg
+ time: mean (interval: 5.0 minutes)
+ calcTCO2_UQL calcTCO2_flags
+
+
+
+
+
+ BamfieldBoL:calcTCO2_Min
+ calcTCO2_Min
+ double
+
+ BoL
+ Burke-o-Lator
+ TCO2_Min
+ μmol/kg
+ time: mininum (interval: 5.0 minutes)
+ calcTCO2_UQL calcTCO2_flags
+
+
+
+
+
+ BamfieldBoL:calcTCO2_Max
+ calcTCO2_Max
+ double
+
+ BoL
+ Burke-o-Lator
+ TCO2_Max
+ μmol/kg
+ time: maximum (interval: 5.0 minutes)
+ calcTCO2_UQL calcTCO2_flags
+
+
+
+
+
+ BamfieldBoL:calcTCO2_Std
+ calcTCO2_Std
+ double
+
+ BoL
+ Burke-o-Lator
+ TCO2_Std
+ μmol/kg
+ time: standard_deviation (interval: 5.0 minutes)
+ calcTCO2_UQL calcTCO2_flags
+
+
+
+
+
+ BamfieldBoL:calcTCO2_UQL
+ calcTCO2_UQL
+ byte
+
+ TCO2 UNESCO Q level
+ PASS NOT_EVALUATED SUSPECT FAIL MISSING
+ 1 2 3 4 9
+ 1
+ aggregate_quality_flag
+ 2
+
+
+
+
+
+ BamfieldBoL:calcTCO2_QC
+ calcTCO2_flags
+ String
+
+ TCO2 Q flags
+ Quality flags
+
+
+
+
+
+ BamfieldBoL:TSG_T_Med
+ TSG_T_Med
+ double
+
+ sea_water_temperature
+ BoL
+ Burke-o-Lator
+ Water temperature_Med
+ degree_Celsius
+ time: median (interval: 5.0 minutes)
+ TSG_T_UQL TSG_T_flags
+
+
+
+
+
+ BamfieldBoL:TSG_T_Avg
+ TSG_T_Avg
+ double
+
+ sea_water_temperature
+ BoL
+ Burke-o-Lator
+ Water temperature_Avg
+ degree_Celsius
+ time: mean (interval: 5.0 minutes)
+ TSG_T_UQL TSG_T_flags
+
+
+
+
+
+ BamfieldBoL:TSG_T_Min
+ TSG_T_Min
+ double
+
+ sea_water_temperature
+ BoL
+ Burke-o-Lator
+ Water temperature_Min
+ degree_Celsius
+ time: mininum (interval: 5.0 minutes)
+ TSG_T_UQL TSG_T_flags
+
+
+
+
+
+ BamfieldBoL:TSG_T_Max
+ TSG_T_Max
+ double
+
+ sea_water_temperature
+ BoL
+ Burke-o-Lator
+ Water temperature_Max
+ degree_Celsius
+ time: maximum (interval: 5.0 minutes)
+ TSG_T_UQL TSG_T_flags
+
+
+
+
+
+ BamfieldBoL:TSG_T_Std
+ TSG_T_Std
+ double
+
+ sea_water_temperature
+ BoL
+ Burke-o-Lator
+ Water temperature_Std
+ degree_Celsius
+ time: standard_deviation (interval: 5.0 minutes)
+ TSG_T_UQL TSG_T_flags
+
+
+
+
+
+ BamfieldBoL:TSG_T_UQL
+ TSG_T_UQL
+ byte
+
+ Water temperature UNESCO Q level
+ PASS NOT_EVALUATED SUSPECT FAIL MISSING
+ 1 2 3 4 9
+ 1
+ aggregate_quality_flag
+ 2
+
+
+
+
+
+ BamfieldBoL:TSG_T_QC
+ TSG_T_flags
+ String
+
+ Water temperature Q flags
+ Quality flags
+
+
+
+
+
+ BamfieldBoL:TSG_S_Med
+ TSG_S_Med
+ double
+
+ sea_water_practical_salinity
+ BoL
+ Burke-o-Lator
+ Salinity_Med
+ time: median (interval: 5.0 minutes)
+ TSG_S_UQL TSG_S_flags
+
+
+
+
+
+ BamfieldBoL:TSG_S_Avg
+ TSG_S_Avg
+ double
+
+ sea_water_practical_salinity
+ BoL
+ Burke-o-Lator
+ Salinity_Avg
+ time: mean (interval: 5.0 minutes)
+ TSG_S_UQL TSG_S_flags
+
+
+
+
+
+ BamfieldBoL:TSG_S_Min
+ TSG_S_Min
+ double
+
+ sea_water_practical_salinity
+ BoL
+ Burke-o-Lator
+ Salinity_Min
+ time: mininum (interval: 5.0 minutes)
+ TSG_S_UQL TSG_S_flags
+
+
+
+
+
+ BamfieldBoL:TSG_S_Max
+ TSG_S_Max
+ double
+
+ sea_water_practical_salinity
+ BoL
+ Burke-o-Lator
+ Salinity_Max
+ time: maximum (interval: 5.0 minutes)
+ TSG_S_UQL TSG_S_flags
+
+
+
+
+
+ BamfieldBoL:TSG_S_Std
+ TSG_S_Std
+ double
+
+ sea_water_practical_salinity
+ BoL
+ Burke-o-Lator
+ Salinity_Std
+ time: standard_deviation (interval: 5.0 minutes)
+ TSG_S_UQL TSG_S_flags
+
+
+
+
+
+ BamfieldBoL:TSG_S_UQL
+ TSG_S_UQL
+ byte
+
+ Salinity UNESCO Q level
+ PASS NOT_EVALUATED SUSPECT FAIL MISSING
+ 1 2 3 4 9
+ 1
+ aggregate_quality_flag
+ 2
+
+
+
+
+
+ BamfieldBoL:TSG_S_QC
+ TSG_S_flags
+ String
+
+ Salinity Q flags
+ Quality flags
+
+
+
+
+
+ BamfieldBoL:AlkS_Med
+ AlkS_Med
+ double
+
+ BoL
+ Burke-o-Lator
+ TA_Med
+ μmol/kg
+ time: median (interval: 5.0 minutes)
+ AlkS_UQL AlkS_flags
+
+
+
+
+
+ BamfieldBoL:AlkS_Avg
+ AlkS_Avg
+ double
+
+ BoL
+ Burke-o-Lator
+ TA_Avg
+ μmol/kg
+ time: mean (interval: 5.0 minutes)
+ AlkS_UQL AlkS_flags
+
+
+
+
+
+ BamfieldBoL:AlkS_Min
+ AlkS_Min
+ double
+
+ BoL
+ Burke-o-Lator
+ TA_Min
+ μmol/kg
+ time: mininum (interval: 5.0 minutes)
+ AlkS_UQL AlkS_flags
+
+
+
+
+
+ BamfieldBoL:AlkS_Max
+ AlkS_Max
+ double
+
+ BoL
+ Burke-o-Lator
+ TA_Max
+ μmol/kg
+ time: maximum (interval: 5.0 minutes)
+ AlkS_UQL AlkS_flags
+
+
+
+
+
+ BamfieldBoL:AlkS_Std
+ AlkS_Std
+ double
+
+ BoL
+ Burke-o-Lator
+ TA_Std
+ μmol/kg
+ time: standard_deviation (interval: 5.0 minutes)
+ AlkS_UQL AlkS_flags
+
+
+
+
+
+ BamfieldBoL:AlkS_UQL
+ AlkS_UQL
+ byte
+
+ TA UNESCO Q level
+ PASS NOT_EVALUATED SUSPECT FAIL MISSING
+ 1 2 3 4 9
+ 1
+ aggregate_quality_flag
+ 2
+
+
+
+
+
+ BamfieldBoL:AlkS_QC
+ AlkS_flags
+ String
+
+ TA Q flags
+ Quality flags
+
+
+
+
+
+ BamfieldBoL:calcOmega_Med
+ calcOmega_Med
+ double
+
+ BoL
+ Burke-o-Lator
+ Ω arag_Med
+ time: median (interval: 5.0 minutes)
+ calcOmega_UQL calcOmega_flags
+
+
+
+
+
+ BamfieldBoL:calcOmega_Avg
+ calcOmega_Avg
+ double
+
+ BoL
+ Burke-o-Lator
+ Ω arag_Avg
+ time: mean (interval: 5.0 minutes)
+ calcOmega_UQL calcOmega_flags
+
+
+
+
+
+ BamfieldBoL:calcOmega_Min
+ calcOmega_Min
+ double
+
+ BoL
+ Burke-o-Lator
+ Ω arag_Min
+ time: mininum (interval: 5.0 minutes)
+ calcOmega_UQL calcOmega_flags
+
+
+
+
+
+ BamfieldBoL:calcOmega_Max
+ calcOmega_Max
+ double
+
+ BoL
+ Burke-o-Lator
+ Ω arag_Max
+ time: maximum (interval: 5.0 minutes)
+ calcOmega_UQL calcOmega_flags
+
+
+
+
+
+ BamfieldBoL:calcOmega_Std
+ calcOmega_Std
+ double
+
+ BoL
+ Burke-o-Lator
+ Ω arag_Std
+ time: standard_deviation (interval: 5.0 minutes)
+ calcOmega_UQL calcOmega_flags
+
+
+
+
+
+ BamfieldBoL:calcOmega_UQL
+ calcOmega_UQL
+ byte
+
+ Ω arag UNESCO Q level
+ PASS NOT_EVALUATED SUSPECT FAIL MISSING
+ 1 2 3 4 9
+ 1
+ aggregate_quality_flag
+ 2
+
+
+
+
+
+ BamfieldBoL:calcOmega_QC
+ calcOmega_flags
+ String
+
+ Ω arag Q flags
+ Quality flags
+
+
+
+
+
+ BamfieldBoL:calcpH_Med
+ calcpH_Med
+ double
+
+ sea_water_ph_reported_on_total_scale
+ BoL
+ Burke-o-Lator
+ pH_T_Med
+ total scale
+ time: median (interval: 5.0 minutes)
+ pCO2_uacalcpH_UQL tm_UQL calcpH_UQL
+
+
+
+
+
+ BamfieldBoL:calcpH_Avg
+ calcpH_Avg
+ double
+
+ sea_water_ph_reported_on_total_scale
+ BoL
+ Burke-o-Lator
+ pH_T_Avg
+ total scale
+ time: mean (interval: 5.0 minutes)
+ pCO2_uacalcpH_UQL tm_UQL calcpH_UQL
+
+
+
+
+
+ BamfieldBoL:calcpH_Min
+ calcpH_Min
+ double
+
+ sea_water_ph_reported_on_total_scale
+ BoL
+ Burke-o-Lator
+ pH_T_Min
+ total scale
+ time: mininum (interval: 5.0 minutes)
+ pCO2_uacalcpH_UQL tm_UQL calcpH_UQL
+
+
+
+
+
+ BamfieldBoL:calcpH_Max
+ calcpH_Max
+ double
+
+ sea_water_ph_reported_on_total_scale
+ BoL
+ Burke-o-Lator
+ pH_T_Max
+ total scale
+ time: maximum (interval: 5.0 minutes)
+ pCO2_uacalcpH_UQL tm_UQL calcpH_UQL
+
+
+
+
+
+ BamfieldBoL:calcpH_Std
+ calcpH_Std
+ double
+
+ sea_water_ph_reported_on_total_scale
+ BoL
+ Burke-o-Lator
+ pH_T_Std
+ total scale
+ time: standard_deviation (interval: 5.0 minutes)
+ pCO2_uacalcpH_UQL tm_UQL calcpH_UQL
+
+
+
+
+
+ BamfieldBoL:calcpH_UQL
+ calcpH_UQL
+ byte
+
+ pH_T UNESCO Q level
+ PASS NOT_EVALUATED SUSPECT FAIL MISSING
+ 1 2 3 4 9
+ 1
+ aggregate_quality_flag
+ 2
+
+
+
+
+
+ BamfieldBoL:calcpH_QC
+ calcpH_flags
+ String
+
+ pH_T Q flags
+ Quality flags
+
+
+
+
+
diff --git a/tests/conftest.py b/tests/conftest.py
index 9d50818..3c87d63 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -1,7 +1,11 @@
+from pathlib import Path
+
import pytest
from hakai_metadata_conversion.__main__ import load
+Path("tests/results").mkdir(exist_ok=True)
+
@pytest.fixture
def record():
diff --git a/tests/erddap_xmls/datasets.d/test_dataset.xml b/tests/erddap_xmls/datasets.d/test_dataset.xml
new file mode 100644
index 0000000..792d641
--- /dev/null
+++ b/tests/erddap_xmls/datasets.d/test_dataset.xml
@@ -0,0 +1,805 @@
+
+
+
+ hakai_erddap_sourceUrl
+ org.postgresql.Driver
+ erddap
+ "HakaiBamfieldBoL5min"
+
+ false
+ CF Standard Name Table v79
+ station
+ TimeSeries
+ station
+ coastal structure
+ http://vocab.nerc.ac.uk/collection/L06/current/
+ Evans, W., Pocock, K., & Weekes, C. (2023). Real-Time Provisional High-Resolution Record of Seawater Carbon Dioxide (CO2) Content Collected from the Nova Harvest Ltd Hatchery in Bamfield BC Canada (Version v).
+ https://creativecommons.org/licenses/by/4.0
+ Dakunalytics, LLC - Burke-o-Lator pCO2/TCO2 analyzer - ,Seabird Electronics - SBE 45 -
+ fb5c9e1e-a911-46b7-8c1d-e34215a105ed
+ ca.cioos
+ Continuous measurement,Discrete measurement,Surface measurement,Ocean Acidification,Practical Salinity,Sea Surface Temperature,Sea Surface Practical Salinity,CO2,Barkley Sound,Trevor Channel
+ land/onshore structure
+ http://vocab.nerc.ac.uk/collection/L06/current/
+ summary
+ title
+ v
+
+
+ measurementTime
+ time
+ String
+
+ time
+ seconds since 1970-01-01T00:00:00Z
+ time: point (interval: 5.0 minutes comment: time correspond to the end of the interval)
+
+
+
+
+
+ =48.836633
+ latitude
+ double
+
+ Fixed latitude of the site
+ Latitude
+ latitude
+ degrees_north
+
+
+
+
+
+ =-125.1362667
+ longitude
+ double
+
+ Fixed longitude of the site
+ Longitude
+ longitude
+ degrees_east
+
+
+
+
+
+ =20
+ depth
+ double
+
+ Approximative depth
+ Depth
+ depth
+ m
+
+
+
+
+ ="Nova Harvest Fisheries Burke-o-Lator"
+ station
+ String
+
+ Location
+ timeseries_id
+
+
+
+
+
+ BamfieldBoL:pCO2_uatm_Med
+ pCO2_uatm_Med
+ double
+
+ partial_pressure_of_carbon_dioxide_in_sea_water
+ BoL
+ Burke-o-Lator
+ pCO2_Med
+ μatm
+ time: median (interval: 5.0 minutes)
+ pCO2_uatm_UQL pCO2_uatm_flags
+
+
+
+
+
+ BamfieldBoL:pCO2_uatm_Avg
+ pCO2_uatm_Avg
+ double
+
+ partial_pressure_of_carbon_dioxide_in_sea_water
+ BoL
+ Burke-o-Lator
+ pCO2_Avg
+ μatm
+ time: mean (interval: 5.0 minutes)
+ pCO2_uatm_UQL pCO2_uatm_flags
+
+
+
+
+
+ BamfieldBoL:pCO2_uatm_Min
+ pCO2_uatm_Min
+ double
+
+ partial_pressure_of_carbon_dioxide_in_sea_water
+ BoL
+ Burke-o-Lator
+ pCO2_Min
+ μatm
+ time: mininum (interval: 5.0 minutes)
+ pCO2_uatm_UQL pCO2_uatm_flags
+
+
+
+
+
+ BamfieldBoL:pCO2_uatm_Max
+ pCO2_uatm_Max
+ double
+
+ partial_pressure_of_carbon_dioxide_in_sea_water
+ BoL
+ Burke-o-Lator
+ pCO2_Max
+ μatm
+ time: maximum (interval: 5.0 minutes)
+ pCO2_uatm_UQL pCO2_uatm_flags
+
+
+
+
+
+ BamfieldBoL:pCO2_uatm_Std
+ pCO2_uatm_Std
+ double
+
+ partial_pressure_of_carbon_dioxide_in_sea_water
+ BoL
+ Burke-o-Lator
+ pCO2_Std
+ μatm
+ time: standard_deviation (interval: 5.0 minutes)
+ pCO2_uatm_UQL pCO2_uatm_flags
+
+
+
+
+
+ BamfieldBoL:pCO2_uatm_UQL
+ pCO2_uatm_UQL
+ byte
+
+ pCO2 UNESCO Q level
+ PASS NOT_EVALUATED SUSPECT FAIL MISSING
+ 1 2 3 4 9
+ 1
+ aggregate_quality_flag
+ 2
+
+
+
+
+
+ BamfieldBoL:pCO2_uatm_QC
+ pCO2_uatm_flags
+ String
+
+ pCO2 Q flags
+ Quality flags
+
+
+
+
+
+ BamfieldBoL:calcTCO2_Med
+ calcTCO2_Med
+ double
+
+ BoL
+ Burke-o-Lator
+ TCO2_Med
+ μmol/kg
+ time: median (interval: 5.0 minutes)
+ calcTCO2_UQL calcTCO2_flags
+
+
+
+
+
+ BamfieldBoL:calcTCO2_Avg
+ calcTCO2_Avg
+ double
+
+ BoL
+ Burke-o-Lator
+ TCO2_Avg
+ μmol/kg
+ time: mean (interval: 5.0 minutes)
+ calcTCO2_UQL calcTCO2_flags
+
+
+
+
+
+ BamfieldBoL:calcTCO2_Min
+ calcTCO2_Min
+ double
+
+ BoL
+ Burke-o-Lator
+ TCO2_Min
+ μmol/kg
+ time: mininum (interval: 5.0 minutes)
+ calcTCO2_UQL calcTCO2_flags
+
+
+
+
+
+ BamfieldBoL:calcTCO2_Max
+ calcTCO2_Max
+ double
+
+ BoL
+ Burke-o-Lator
+ TCO2_Max
+ μmol/kg
+ time: maximum (interval: 5.0 minutes)
+ calcTCO2_UQL calcTCO2_flags
+
+
+
+
+
+ BamfieldBoL:calcTCO2_Std
+ calcTCO2_Std
+ double
+
+ BoL
+ Burke-o-Lator
+ TCO2_Std
+ μmol/kg
+ time: standard_deviation (interval: 5.0 minutes)
+ calcTCO2_UQL calcTCO2_flags
+
+
+
+
+
+ BamfieldBoL:calcTCO2_UQL
+ calcTCO2_UQL
+ byte
+
+ TCO2 UNESCO Q level
+ PASS NOT_EVALUATED SUSPECT FAIL MISSING
+ 1 2 3 4 9
+ 1
+ aggregate_quality_flag
+ 2
+
+
+
+
+
+ BamfieldBoL:calcTCO2_QC
+ calcTCO2_flags
+ String
+
+ TCO2 Q flags
+ Quality flags
+
+
+
+
+
+ BamfieldBoL:TSG_T_Med
+ TSG_T_Med
+ double
+
+ sea_water_temperature
+ BoL
+ Burke-o-Lator
+ Water temperature_Med
+ degree_Celsius
+ time: median (interval: 5.0 minutes)
+ TSG_T_UQL TSG_T_flags
+
+
+
+
+
+ BamfieldBoL:TSG_T_Avg
+ TSG_T_Avg
+ double
+
+ sea_water_temperature
+ BoL
+ Burke-o-Lator
+ Water temperature_Avg
+ degree_Celsius
+ time: mean (interval: 5.0 minutes)
+ TSG_T_UQL TSG_T_flags
+
+
+
+
+
+ BamfieldBoL:TSG_T_Min
+ TSG_T_Min
+ double
+
+ sea_water_temperature
+ BoL
+ Burke-o-Lator
+ Water temperature_Min
+ degree_Celsius
+ time: mininum (interval: 5.0 minutes)
+ TSG_T_UQL TSG_T_flags
+
+
+
+
+
+ BamfieldBoL:TSG_T_Max
+ TSG_T_Max
+ double
+
+ sea_water_temperature
+ BoL
+ Burke-o-Lator
+ Water temperature_Max
+ degree_Celsius
+ time: maximum (interval: 5.0 minutes)
+ TSG_T_UQL TSG_T_flags
+
+
+
+
+
+ BamfieldBoL:TSG_T_Std
+ TSG_T_Std
+ double
+
+ sea_water_temperature
+ BoL
+ Burke-o-Lator
+ Water temperature_Std
+ degree_Celsius
+ time: standard_deviation (interval: 5.0 minutes)
+ TSG_T_UQL TSG_T_flags
+
+
+
+
+
+ BamfieldBoL:TSG_T_UQL
+ TSG_T_UQL
+ byte
+
+ Water temperature UNESCO Q level
+ PASS NOT_EVALUATED SUSPECT FAIL MISSING
+ 1 2 3 4 9
+ 1
+ aggregate_quality_flag
+ 2
+
+
+
+
+
+ BamfieldBoL:TSG_T_QC
+ TSG_T_flags
+ String
+
+ Water temperature Q flags
+ Quality flags
+
+
+
+
+
+ BamfieldBoL:TSG_S_Med
+ TSG_S_Med
+ double
+
+ sea_water_practical_salinity
+ BoL
+ Burke-o-Lator
+ Salinity_Med
+ time: median (interval: 5.0 minutes)
+ TSG_S_UQL TSG_S_flags
+
+
+
+
+
+ BamfieldBoL:TSG_S_Avg
+ TSG_S_Avg
+ double
+
+ sea_water_practical_salinity
+ BoL
+ Burke-o-Lator
+ Salinity_Avg
+ time: mean (interval: 5.0 minutes)
+ TSG_S_UQL TSG_S_flags
+
+
+
+
+
+ BamfieldBoL:TSG_S_Min
+ TSG_S_Min
+ double
+
+ sea_water_practical_salinity
+ BoL
+ Burke-o-Lator
+ Salinity_Min
+ time: mininum (interval: 5.0 minutes)
+ TSG_S_UQL TSG_S_flags
+
+
+
+
+
+ BamfieldBoL:TSG_S_Max
+ TSG_S_Max
+ double
+
+ sea_water_practical_salinity
+ BoL
+ Burke-o-Lator
+ Salinity_Max
+ time: maximum (interval: 5.0 minutes)
+ TSG_S_UQL TSG_S_flags
+
+
+
+
+
+ BamfieldBoL:TSG_S_Std
+ TSG_S_Std
+ double
+
+ sea_water_practical_salinity
+ BoL
+ Burke-o-Lator
+ Salinity_Std
+ time: standard_deviation (interval: 5.0 minutes)
+ TSG_S_UQL TSG_S_flags
+
+
+
+
+
+ BamfieldBoL:TSG_S_UQL
+ TSG_S_UQL
+ byte
+
+ Salinity UNESCO Q level
+ PASS NOT_EVALUATED SUSPECT FAIL MISSING
+ 1 2 3 4 9
+ 1
+ aggregate_quality_flag
+ 2
+
+
+
+
+
+ BamfieldBoL:TSG_S_QC
+ TSG_S_flags
+ String
+
+ Salinity Q flags
+ Quality flags
+
+
+
+
+
+ BamfieldBoL:AlkS_Med
+ AlkS_Med
+ double
+
+ BoL
+ Burke-o-Lator
+ TA_Med
+ μmol/kg
+ time: median (interval: 5.0 minutes)
+ AlkS_UQL AlkS_flags
+
+
+
+
+
+ BamfieldBoL:AlkS_Avg
+ AlkS_Avg
+ double
+
+ BoL
+ Burke-o-Lator
+ TA_Avg
+ μmol/kg
+ time: mean (interval: 5.0 minutes)
+ AlkS_UQL AlkS_flags
+
+
+
+
+
+ BamfieldBoL:AlkS_Min
+ AlkS_Min
+ double
+
+ BoL
+ Burke-o-Lator
+ TA_Min
+ μmol/kg
+ time: mininum (interval: 5.0 minutes)
+ AlkS_UQL AlkS_flags
+
+
+
+
+
+ BamfieldBoL:AlkS_Max
+ AlkS_Max
+ double
+
+ BoL
+ Burke-o-Lator
+ TA_Max
+ μmol/kg
+ time: maximum (interval: 5.0 minutes)
+ AlkS_UQL AlkS_flags
+
+
+
+
+
+ BamfieldBoL:AlkS_Std
+ AlkS_Std
+ double
+
+ BoL
+ Burke-o-Lator
+ TA_Std
+ μmol/kg
+ time: standard_deviation (interval: 5.0 minutes)
+ AlkS_UQL AlkS_flags
+
+
+
+
+
+ BamfieldBoL:AlkS_UQL
+ AlkS_UQL
+ byte
+
+ TA UNESCO Q level
+ PASS NOT_EVALUATED SUSPECT FAIL MISSING
+ 1 2 3 4 9
+ 1
+ aggregate_quality_flag
+ 2
+
+
+
+
+
+ BamfieldBoL:AlkS_QC
+ AlkS_flags
+ String
+
+ TA Q flags
+ Quality flags
+
+
+
+
+
+ BamfieldBoL:calcOmega_Med
+ calcOmega_Med
+ double
+
+ BoL
+ Burke-o-Lator
+ Ω arag_Med
+ time: median (interval: 5.0 minutes)
+ calcOmega_UQL calcOmega_flags
+
+
+
+
+
+ BamfieldBoL:calcOmega_Avg
+ calcOmega_Avg
+ double
+
+ BoL
+ Burke-o-Lator
+ Ω arag_Avg
+ time: mean (interval: 5.0 minutes)
+ calcOmega_UQL calcOmega_flags
+
+
+
+
+
+ BamfieldBoL:calcOmega_Min
+ calcOmega_Min
+ double
+
+ BoL
+ Burke-o-Lator
+ Ω arag_Min
+ time: mininum (interval: 5.0 minutes)
+ calcOmega_UQL calcOmega_flags
+
+
+
+
+
+ BamfieldBoL:calcOmega_Max
+ calcOmega_Max
+ double
+
+ BoL
+ Burke-o-Lator
+ Ω arag_Max
+ time: maximum (interval: 5.0 minutes)
+ calcOmega_UQL calcOmega_flags
+
+
+
+
+
+ BamfieldBoL:calcOmega_Std
+ calcOmega_Std
+ double
+
+ BoL
+ Burke-o-Lator
+ Ω arag_Std
+ time: standard_deviation (interval: 5.0 minutes)
+ calcOmega_UQL calcOmega_flags
+
+
+
+
+
+ BamfieldBoL:calcOmega_UQL
+ calcOmega_UQL
+ byte
+
+ Ω arag UNESCO Q level
+ PASS NOT_EVALUATED SUSPECT FAIL MISSING
+ 1 2 3 4 9
+ 1
+ aggregate_quality_flag
+ 2
+
+
+
+
+
+ BamfieldBoL:calcOmega_QC
+ calcOmega_flags
+ String
+
+ Ω arag Q flags
+ Quality flags
+
+
+
+
+
+ BamfieldBoL:calcpH_Med
+ calcpH_Med
+ double
+
+ sea_water_ph_reported_on_total_scale
+ BoL
+ Burke-o-Lator
+ pH_T_Med
+ total scale
+ time: median (interval: 5.0 minutes)
+ pCO2_uacalcpH_UQL tm_UQL calcpH_UQL
+
+
+
+
+
+ BamfieldBoL:calcpH_Avg
+ calcpH_Avg
+ double
+
+ sea_water_ph_reported_on_total_scale
+ BoL
+ Burke-o-Lator
+ pH_T_Avg
+ total scale
+ time: mean (interval: 5.0 minutes)
+ pCO2_uacalcpH_UQL tm_UQL calcpH_UQL
+
+
+
+
+
+ BamfieldBoL:calcpH_Min
+ calcpH_Min
+ double
+
+ sea_water_ph_reported_on_total_scale
+ BoL
+ Burke-o-Lator
+ pH_T_Min
+ total scale
+ time: mininum (interval: 5.0 minutes)
+ pCO2_uacalcpH_UQL tm_UQL calcpH_UQL
+
+
+
+
+
+ BamfieldBoL:calcpH_Max
+ calcpH_Max
+ double
+
+ sea_water_ph_reported_on_total_scale
+ BoL
+ Burke-o-Lator
+ pH_T_Max
+ total scale
+ time: maximum (interval: 5.0 minutes)
+ pCO2_uacalcpH_UQL tm_UQL calcpH_UQL
+
+
+
+
+
+ BamfieldBoL:calcpH_Std
+ calcpH_Std
+ double
+
+ sea_water_ph_reported_on_total_scale
+ BoL
+ Burke-o-Lator
+ pH_T_Std
+ total scale
+ time: standard_deviation (interval: 5.0 minutes)
+ pCO2_uacalcpH_UQL tm_UQL calcpH_UQL
+
+
+
+
+
+ BamfieldBoL:calcpH_UQL
+ calcpH_UQL
+ byte
+
+ pH_T UNESCO Q level
+ PASS NOT_EVALUATED SUSPECT FAIL MISSING
+ 1 2 3 4 9
+ 1
+ aggregate_quality_flag
+ 2
+
+
+
+
+
+ BamfieldBoL:calcpH_QC
+ calcpH_flags
+ String
+
+ pH_T Q flags
+ Quality flags
+
+
+
+
\ No newline at end of file
diff --git a/tests/erddap_xmls/test_datasets.xml b/tests/erddap_xmls/test_datasets.xml
new file mode 100644
index 0000000..2e1d905
--- /dev/null
+++ b/tests/erddap_xmls/test_datasets.xml
@@ -0,0 +1,807 @@
+
+
+
+ hakai_erddap_sourceUrl
+ org.postgresql.Driver
+ erddap
+ "TestDataset1"
+
+ false
+ https://www.hakai.org
+ CF Standard Name Table v79
+ station
+ TimeSeries
+ station
+ coastal structure
+ http://vocab.nerc.ac.uk/collection/L06/current/
+ Oceanography
+ https://creativecommons.org/licenses/by/4.0
+ Dakunalytics, LLC - Burke-o-Lator pCO2/TCO2 analyzer - ,Seabird Electronics - SBE 45 -
+ fb5c9e1e-a911-46b7-8c1d-e34215a105ed
+ ca.cioos
+ some,key,words
+ land/onshore structure
+ http://vocab.nerc.ac.uk/collection/L06/current/
+ summary
+ title
+
+
+
+ measurementTime
+ time
+ String
+
+ time
+ seconds since 1970-01-01T00:00:00Z
+ time: point (interval: 5.0 minutes comment: time correspond to the end of the interval)
+
+
+
+
+
+ =48.836633
+ latitude
+ double
+
+ Fixed latitude of the site
+ Latitude
+ latitude
+ degrees_north
+
+
+
+
+
+ =-125.1362667
+ longitude
+ double
+
+ Fixed longitude of the site
+ Longitude
+ longitude
+ degrees_east
+
+
+
+
+
+ =20
+ depth
+ double
+
+ Approximative depth
+ Depth
+ depth
+ m
+
+
+
+
+ ="Nova Harvest Fisheries Burke-o-Lator"
+ station
+ String
+
+ Location
+ timeseries_id
+
+
+
+
+
+ BamfieldBoL:pCO2_uatm_Med
+ pCO2_uatm_Med
+ double
+
+ partial_pressure_of_carbon_dioxide_in_sea_water
+ BoL
+ Burke-o-Lator
+ pCO2_Med
+ μatm
+ time: median (interval: 5.0 minutes)
+ pCO2_uatm_UQL pCO2_uatm_flags
+
+
+
+
+
+ BamfieldBoL:pCO2_uatm_Avg
+ pCO2_uatm_Avg
+ double
+
+ partial_pressure_of_carbon_dioxide_in_sea_water
+ BoL
+ Burke-o-Lator
+ pCO2_Avg
+ μatm
+ time: mean (interval: 5.0 minutes)
+ pCO2_uatm_UQL pCO2_uatm_flags
+
+
+
+
+
+ BamfieldBoL:pCO2_uatm_Min
+ pCO2_uatm_Min
+ double
+
+ partial_pressure_of_carbon_dioxide_in_sea_water
+ BoL
+ Burke-o-Lator
+ pCO2_Min
+ μatm
+ time: mininum (interval: 5.0 minutes)
+ pCO2_uatm_UQL pCO2_uatm_flags
+
+
+
+
+
+ BamfieldBoL:pCO2_uatm_Max
+ pCO2_uatm_Max
+ double
+
+ partial_pressure_of_carbon_dioxide_in_sea_water
+ BoL
+ Burke-o-Lator
+ pCO2_Max
+ μatm
+ time: maximum (interval: 5.0 minutes)
+ pCO2_uatm_UQL pCO2_uatm_flags
+
+
+
+
+
+ BamfieldBoL:pCO2_uatm_Std
+ pCO2_uatm_Std
+ double
+
+ partial_pressure_of_carbon_dioxide_in_sea_water
+ BoL
+ Burke-o-Lator
+ pCO2_Std
+ μatm
+ time: standard_deviation (interval: 5.0 minutes)
+ pCO2_uatm_UQL pCO2_uatm_flags
+
+
+
+
+
+ BamfieldBoL:pCO2_uatm_UQL
+ pCO2_uatm_UQL
+ byte
+
+ pCO2 UNESCO Q level
+ PASS NOT_EVALUATED SUSPECT FAIL MISSING
+ 1 2 3 4 9
+ 1
+ aggregate_quality_flag
+ 2
+
+
+
+
+
+ BamfieldBoL:pCO2_uatm_QC
+ pCO2_uatm_flags
+ String
+
+ pCO2 Q flags
+ Quality flags
+
+
+
+
+
+ BamfieldBoL:calcTCO2_Med
+ calcTCO2_Med
+ double
+
+ BoL
+ Burke-o-Lator
+ TCO2_Med
+ μmol/kg
+ time: median (interval: 5.0 minutes)
+ calcTCO2_UQL calcTCO2_flags
+
+
+
+
+
+ BamfieldBoL:calcTCO2_Avg
+ calcTCO2_Avg
+ double
+
+ BoL
+ Burke-o-Lator
+ TCO2_Avg
+ μmol/kg
+ time: mean (interval: 5.0 minutes)
+ calcTCO2_UQL calcTCO2_flags
+
+
+
+
+
+ BamfieldBoL:calcTCO2_Min
+ calcTCO2_Min
+ double
+
+ BoL
+ Burke-o-Lator
+ TCO2_Min
+ μmol/kg
+ time: mininum (interval: 5.0 minutes)
+ calcTCO2_UQL calcTCO2_flags
+
+
+
+
+
+ BamfieldBoL:calcTCO2_Max
+ calcTCO2_Max
+ double
+
+ BoL
+ Burke-o-Lator
+ TCO2_Max
+ μmol/kg
+ time: maximum (interval: 5.0 minutes)
+ calcTCO2_UQL calcTCO2_flags
+
+
+
+
+
+ BamfieldBoL:calcTCO2_Std
+ calcTCO2_Std
+ double
+
+ BoL
+ Burke-o-Lator
+ TCO2_Std
+ μmol/kg
+ time: standard_deviation (interval: 5.0 minutes)
+ calcTCO2_UQL calcTCO2_flags
+
+
+
+
+
+ BamfieldBoL:calcTCO2_UQL
+ calcTCO2_UQL
+ byte
+
+ TCO2 UNESCO Q level
+ PASS NOT_EVALUATED SUSPECT FAIL MISSING
+ 1 2 3 4 9
+ 1
+ aggregate_quality_flag
+ 2
+
+
+
+
+
+ BamfieldBoL:calcTCO2_QC
+ calcTCO2_flags
+ String
+
+ TCO2 Q flags
+ Quality flags
+
+
+
+
+
+ BamfieldBoL:TSG_T_Med
+ TSG_T_Med
+ double
+
+ sea_water_temperature
+ BoL
+ Burke-o-Lator
+ Water temperature_Med
+ degree_Celsius
+ time: median (interval: 5.0 minutes)
+ TSG_T_UQL TSG_T_flags
+
+
+
+
+
+ BamfieldBoL:TSG_T_Avg
+ TSG_T_Avg
+ double
+
+ sea_water_temperature
+ BoL
+ Burke-o-Lator
+ Water temperature_Avg
+ degree_Celsius
+ time: mean (interval: 5.0 minutes)
+ TSG_T_UQL TSG_T_flags
+
+
+
+
+
+ BamfieldBoL:TSG_T_Min
+ TSG_T_Min
+ double
+
+ sea_water_temperature
+ BoL
+ Burke-o-Lator
+ Water temperature_Min
+ degree_Celsius
+ time: mininum (interval: 5.0 minutes)
+ TSG_T_UQL TSG_T_flags
+
+
+
+
+
+ BamfieldBoL:TSG_T_Max
+ TSG_T_Max
+ double
+
+ sea_water_temperature
+ BoL
+ Burke-o-Lator
+ Water temperature_Max
+ degree_Celsius
+ time: maximum (interval: 5.0 minutes)
+ TSG_T_UQL TSG_T_flags
+
+
+
+
+
+ BamfieldBoL:TSG_T_Std
+ TSG_T_Std
+ double
+
+ sea_water_temperature
+ BoL
+ Burke-o-Lator
+ Water temperature_Std
+ degree_Celsius
+ time: standard_deviation (interval: 5.0 minutes)
+ TSG_T_UQL TSG_T_flags
+
+
+
+
+
+ BamfieldBoL:TSG_T_UQL
+ TSG_T_UQL
+ byte
+
+ Water temperature UNESCO Q level
+ PASS NOT_EVALUATED SUSPECT FAIL MISSING
+ 1 2 3 4 9
+ 1
+ aggregate_quality_flag
+ 2
+
+
+
+
+
+ BamfieldBoL:TSG_T_QC
+ TSG_T_flags
+ String
+
+ Water temperature Q flags
+ Quality flags
+
+
+
+
+
+ BamfieldBoL:TSG_S_Med
+ TSG_S_Med
+ double
+
+ sea_water_practical_salinity
+ BoL
+ Burke-o-Lator
+ Salinity_Med
+ time: median (interval: 5.0 minutes)
+ TSG_S_UQL TSG_S_flags
+
+
+
+
+
+ BamfieldBoL:TSG_S_Avg
+ TSG_S_Avg
+ double
+
+ sea_water_practical_salinity
+ BoL
+ Burke-o-Lator
+ Salinity_Avg
+ time: mean (interval: 5.0 minutes)
+ TSG_S_UQL TSG_S_flags
+
+
+
+
+
+ BamfieldBoL:TSG_S_Min
+ TSG_S_Min
+ double
+
+ sea_water_practical_salinity
+ BoL
+ Burke-o-Lator
+ Salinity_Min
+ time: mininum (interval: 5.0 minutes)
+ TSG_S_UQL TSG_S_flags
+
+
+
+
+
+ BamfieldBoL:TSG_S_Max
+ TSG_S_Max
+ double
+
+ sea_water_practical_salinity
+ BoL
+ Burke-o-Lator
+ Salinity_Max
+ time: maximum (interval: 5.0 minutes)
+ TSG_S_UQL TSG_S_flags
+
+
+
+
+
+ BamfieldBoL:TSG_S_Std
+ TSG_S_Std
+ double
+
+ sea_water_practical_salinity
+ BoL
+ Burke-o-Lator
+ Salinity_Std
+ time: standard_deviation (interval: 5.0 minutes)
+ TSG_S_UQL TSG_S_flags
+
+
+
+
+
+ BamfieldBoL:TSG_S_UQL
+ TSG_S_UQL
+ byte
+
+ Salinity UNESCO Q level
+ PASS NOT_EVALUATED SUSPECT FAIL MISSING
+ 1 2 3 4 9
+ 1
+ aggregate_quality_flag
+ 2
+
+
+
+
+
+ BamfieldBoL:TSG_S_QC
+ TSG_S_flags
+ String
+
+ Salinity Q flags
+ Quality flags
+
+
+
+
+
+ BamfieldBoL:AlkS_Med
+ AlkS_Med
+ double
+
+ BoL
+ Burke-o-Lator
+ TA_Med
+ μmol/kg
+ time: median (interval: 5.0 minutes)
+ AlkS_UQL AlkS_flags
+
+
+
+
+
+ BamfieldBoL:AlkS_Avg
+ AlkS_Avg
+ double
+
+ BoL
+ Burke-o-Lator
+ TA_Avg
+ μmol/kg
+ time: mean (interval: 5.0 minutes)
+ AlkS_UQL AlkS_flags
+
+
+
+
+
+ BamfieldBoL:AlkS_Min
+ AlkS_Min
+ double
+
+ BoL
+ Burke-o-Lator
+ TA_Min
+ μmol/kg
+ time: mininum (interval: 5.0 minutes)
+ AlkS_UQL AlkS_flags
+
+
+
+
+
+ BamfieldBoL:AlkS_Max
+ AlkS_Max
+ double
+
+ BoL
+ Burke-o-Lator
+ TA_Max
+ μmol/kg
+ time: maximum (interval: 5.0 minutes)
+ AlkS_UQL AlkS_flags
+
+
+
+
+
+ BamfieldBoL:AlkS_Std
+ AlkS_Std
+ double
+
+ BoL
+ Burke-o-Lator
+ TA_Std
+ μmol/kg
+ time: standard_deviation (interval: 5.0 minutes)
+ AlkS_UQL AlkS_flags
+
+
+
+
+
+ BamfieldBoL:AlkS_UQL
+ AlkS_UQL
+ byte
+
+ TA UNESCO Q level
+ PASS NOT_EVALUATED SUSPECT FAIL MISSING
+ 1 2 3 4 9
+ 1
+ aggregate_quality_flag
+ 2
+
+
+
+
+
+ BamfieldBoL:AlkS_QC
+ AlkS_flags
+ String
+
+ TA Q flags
+ Quality flags
+
+
+
+
+
+ BamfieldBoL:calcOmega_Med
+ calcOmega_Med
+ double
+
+ BoL
+ Burke-o-Lator
+ Ω arag_Med
+ time: median (interval: 5.0 minutes)
+ calcOmega_UQL calcOmega_flags
+
+
+
+
+
+ BamfieldBoL:calcOmega_Avg
+ calcOmega_Avg
+ double
+
+ BoL
+ Burke-o-Lator
+ Ω arag_Avg
+ time: mean (interval: 5.0 minutes)
+ calcOmega_UQL calcOmega_flags
+
+
+
+
+
+ BamfieldBoL:calcOmega_Min
+ calcOmega_Min
+ double
+
+ BoL
+ Burke-o-Lator
+ Ω arag_Min
+ time: mininum (interval: 5.0 minutes)
+ calcOmega_UQL calcOmega_flags
+
+
+
+
+
+ BamfieldBoL:calcOmega_Max
+ calcOmega_Max
+ double
+
+ BoL
+ Burke-o-Lator
+ Ω arag_Max
+ time: maximum (interval: 5.0 minutes)
+ calcOmega_UQL calcOmega_flags
+
+
+
+
+
+ BamfieldBoL:calcOmega_Std
+ calcOmega_Std
+ double
+
+ BoL
+ Burke-o-Lator
+ Ω arag_Std
+ time: standard_deviation (interval: 5.0 minutes)
+ calcOmega_UQL calcOmega_flags
+
+
+
+
+
+ BamfieldBoL:calcOmega_UQL
+ calcOmega_UQL
+ byte
+
+ Ω arag UNESCO Q level
+ PASS NOT_EVALUATED SUSPECT FAIL MISSING
+ 1 2 3 4 9
+ 1
+ aggregate_quality_flag
+ 2
+
+
+
+
+
+ BamfieldBoL:calcOmega_QC
+ calcOmega_flags
+ String
+
+ Ω arag Q flags
+ Quality flags
+
+
+
+
+
+ BamfieldBoL:calcpH_Med
+ calcpH_Med
+ double
+
+ sea_water_ph_reported_on_total_scale
+ BoL
+ Burke-o-Lator
+ pH_T_Med
+ total scale
+ time: median (interval: 5.0 minutes)
+ pCO2_uacalcpH_UQL tm_UQL calcpH_UQL
+
+
+
+
+
+ BamfieldBoL:calcpH_Avg
+ calcpH_Avg
+ double
+
+ sea_water_ph_reported_on_total_scale
+ BoL
+ Burke-o-Lator
+ pH_T_Avg
+ total scale
+ time: mean (interval: 5.0 minutes)
+ pCO2_uacalcpH_UQL tm_UQL calcpH_UQL
+
+
+
+
+
+ BamfieldBoL:calcpH_Min
+ calcpH_Min
+ double
+
+ sea_water_ph_reported_on_total_scale
+ BoL
+ Burke-o-Lator
+ pH_T_Min
+ total scale
+ time: mininum (interval: 5.0 minutes)
+ pCO2_uacalcpH_UQL tm_UQL calcpH_UQL
+
+
+
+
+
+ BamfieldBoL:calcpH_Max
+ calcpH_Max
+ double
+
+ sea_water_ph_reported_on_total_scale
+ BoL
+ Burke-o-Lator
+ pH_T_Max
+ total scale
+ time: maximum (interval: 5.0 minutes)
+ pCO2_uacalcpH_UQL tm_UQL calcpH_UQL
+
+
+
+
+
+ BamfieldBoL:calcpH_Std
+ calcpH_Std
+ double
+
+ sea_water_ph_reported_on_total_scale
+ BoL
+ Burke-o-Lator
+ pH_T_Std
+ total scale
+ time: standard_deviation (interval: 5.0 minutes)
+ pCO2_uacalcpH_UQL tm_UQL calcpH_UQL
+
+
+
+
+
+ BamfieldBoL:calcpH_UQL
+ calcpH_UQL
+ byte
+
+ pH_T UNESCO Q level
+ PASS NOT_EVALUATED SUSPECT FAIL MISSING
+ 1 2 3 4 9
+ 1
+ aggregate_quality_flag
+ 2
+
+
+
+
+
+ BamfieldBoL:calcpH_QC
+ calcpH_flags
+ String
+
+ pH_T Q flags
+ Quality flags
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/records/test_record1.yaml b/tests/records/test_record1.yaml
index fa514c1..a5e674f 100644
--- a/tests/records/test_record1.yaml
+++ b/tests/records/test_record1.yaml
@@ -63,7 +63,7 @@ distribution:
verified: false
name:
en: ERDDAP Dataset
- url: https://catalogue.hakai.org/erddap/tabledap/HakaiWaterPropertiesInstrumentProfileResearch.html
+ url: https://catalogue.hakai.org/erddap/tabledap/TestDataset1.html
- name:
en: Hakai Water Properties Profile Processing and QA/QC Procedure Manual
url: https://github.com/HakaiInstitute/hakai-datasets/raw/development/datasets_documents/HakaiWaterPropertiesProfiles/Hakai_Water_Properties_Processing_and_QAQC_Procedure_20210331.pdf
diff --git a/tests/test_citation_cff.py b/tests/test_citation_cff.py
index 105375d..a8f5da0 100644
--- a/tests/test_citation_cff.py
+++ b/tests/test_citation_cff.py
@@ -1,6 +1,5 @@
import subprocess
from glob import glob
-from pathlib import Path
import pytest
@@ -27,9 +26,9 @@ def test_citation_cff(record):
assert "identifiers" in result
-def test_ctation_cff_yaml(record):
+def test_ctation_cff_yaml(record, tmp_path):
result = citation_cff.citation_cff(record, output_format="yaml", language="en")
- Path("CITATION.cff").write_text(result, encoding="utf-8")
+ (tmp_path / "CITATION.cff").write_text(result, encoding="utf-8")
def test_citation_cff_validation(record, tmp_path):
@@ -55,10 +54,11 @@ def test_hakai_metadata_entry_form_files_cff(file, tmp_path):
# validate cff
(tmp_path / "CITATION.cff").write_text(result, encoding="utf-8")
- result = subprocess.run(
+ validation_result = subprocess.run(
["cffconvert", "--validate", "-i", str(tmp_path / "CITATION.cff")],
capture_output=True,
)
+ assert validation_result.returncode == 0, validation_result.stderr.decode("utf-8")
@pytest.mark.parametrize(
@@ -72,7 +72,8 @@ def test_hakai_metadata_entry_form_files_cff_fr(file, tmp_path):
# validate cff
(tmp_path / "CITATION.cff").write_text(result_fr, encoding="utf-8")
- result = subprocess.run(
+ validation_result = subprocess.run(
["cffconvert", "--validate", "-i", str(tmp_path / "CITATION.cff")],
capture_output=True,
)
+ assert validation_result.returncode == 0, validation_result.stderr.decode("utf-8")
diff --git a/tests/test_cli.py b/tests/test_cli.py
index 85d7cd1..a21ae76 100644
--- a/tests/test_cli.py
+++ b/tests/test_cli.py
@@ -3,7 +3,7 @@
import pytest
from click.testing import CliRunner
-from hakai_metadata_conversion.__main__ import cli_main
+from hakai_metadata_conversion.__main__ import cli
@pytest.fixture
@@ -12,21 +12,22 @@ def runner():
def test_cli_no_args(runner):
- result = runner.invoke(cli_main)
- assert result.exit_code == 2
- assert "Error: Missing option '--input'" in result.output
+ result = runner.invoke(cli)
+ assert result.exit_code == 0
+ assert "Usage: hakai-metadata-conversion [OPTIONS] COMMAND [ARGS]" in result.output
def test_cli_help(runner):
- result = runner.invoke(cli_main, ["--help"])
+ result = runner.invoke(cli, ["--help"])
assert result.exit_code == 0
- assert "Usage: cli-main [OPTIONS]" in result.output
+ assert "Usage: hakai-metadata-conversion [OPTIONS]" in result.output
def test_cli_on_test_files(runner, tmpdir):
input_files = "tests/records/*.yaml"
n_test_files = len(glob(input_files))
args = [
+ "convert",
"--input",
input_files,
"--input-file-format",
@@ -36,14 +37,15 @@ def test_cli_on_test_files(runner, tmpdir):
"--output-dir",
str(tmpdir),
]
- result = runner.invoke(cli_main, args)
- assert result.exit_code == 0
+ result = runner.invoke(cli, args)
+ assert result.exit_code == 0, result.output
assert result.output == ""
assert len(tmpdir.listdir()) == n_test_files
def test_cli_output_file(runner, tmpdir):
args = [
+ "convert",
"--input",
"tests/records/*.yaml",
"--input-file-format",
@@ -53,7 +55,7 @@ def test_cli_output_file(runner, tmpdir):
"--output-file",
str(tmpdir / "CITATION.cff"),
]
- result = runner.invoke(cli_main, args)
+ result = runner.invoke(cli, args)
assert result.exit_code == 0
assert result.output == ""
assert len(tmpdir.listdir()) == 1
@@ -66,6 +68,7 @@ def test_cli_output_file(runner, tmpdir):
def test_cli_with_http_input(runner, tmpdir):
ouput_file = "CITATION.cff"
args = [
+ "convert",
"--input",
"https://raw.githubusercontent.com/HakaiInstitute/hakai-metadata-conversion/main/tests/records/test_record1.yaml",
"--input-file-format",
@@ -75,8 +78,8 @@ def test_cli_with_http_input(runner, tmpdir):
"--output-file",
str(tmpdir / ouput_file),
]
- result = runner.invoke(cli_main, args)
- assert result.exit_code == 0
+ result = runner.invoke(cli, args)
+ assert result.exit_code == 0, result.output
assert result.output == ""
assert len(tmpdir.listdir()) == 1
assert tmpdir.join(ouput_file).check(file=True)
diff --git a/tests/test_erddap.py b/tests/test_erddap.py
index 5ccbae2..c0676cf 100644
--- a/tests/test_erddap.py
+++ b/tests/test_erddap.py
@@ -2,12 +2,12 @@
import pytest
+import hakai_metadata_conversion.erddap as erddap
from hakai_metadata_conversion.__main__ import load
-from hakai_metadata_conversion.erddap import global_attributes
def test_erddap_global_attributes(record):
- result = global_attributes(record, output=None, language="en")
+ result = erddap.global_attributes(record, output=None, language="en")
assert result
assert isinstance(result, dict)
assert "title" in result
@@ -36,8 +36,8 @@ def test_erddap_global_attributes(record):
assert "metadata_link" in result
-def test_erddap_global_attirbutes_xml(record):
- result = global_attributes(record, output="xml", language="en")
+def test_erddap_global_attributes_xml(record):
+ result = erddap.global_attributes(record, output="xml", language="en")
assert result
@@ -47,7 +47,7 @@ def test_erddap_global_attirbutes_xml(record):
)
def test_hakai_metadata_files_to_erddap(file):
data = load(file, "yaml")
- result = global_attributes(data, output="xml", language="en")
+ result = erddap.global_attributes(data, output="xml", language="en")
assert result
@@ -58,6 +58,37 @@ def test_hakai_metadata_files_to_erddap(file):
)
def test_hakai_metadata_files_to_erddap_fr(file):
data = load(file, "yaml")
- result_fr = global_attributes(data, output="xml", language="fr")
+ result_fr = erddap.global_attributes(data, output="xml", language="fr")
assert result_fr
+
+
+def test_erddap_dataset_xml_update(record, tmp_path):
+ erddap.update_dataset_xml(
+ "tests/erddap_xmls/test_datasets.xml",
+ [record],
+ erddap_url="https://catalogue.hakai.org/erddap",
+ output_dir=tmp_path,
+ )
+ assert (tmp_path / "test_datasets.xml").exists()
+
+
+def test_erddap_dataset_xml_update_string(tmp_path):
+ erddap.update_dataset_xml(
+ "tests/erddap_xmls/test_datasets.xml",
+ "tests/records/*.yaml",
+ erddap_url="https://catalogue.hakai.org/erddap",
+ output_dir=tmp_path,
+ )
+ assert (tmp_path / "test_datasets.xml").exists()
+
+
+def test_erddap_dataset_d_xml_update(record, tmp_path):
+ erddap.update_dataset_xml(
+ "tests/erddap_xmls/dataset.d/*.xml",
+ [record],
+ erddap_url="https://catalogue.hakai.org/erddap",
+ output_dir=tmp_path,
+ )
+ files = tmp_path.glob("dataset.d/*.xml")
+ assert files
diff --git a/tests/test_xml.py b/tests/test_xml.py
index 3759de8..ad20aff 100644
--- a/tests/test_xml.py
+++ b/tests/test_xml.py
@@ -1,17 +1,17 @@
-import pytest
from glob import glob
-from pathlib import Path
+
+import pytest
from hakai_metadata_conversion.__main__ import load
from hakai_metadata_conversion.xml import xml
+
def test_xml(record):
result = xml(record)
assert result
assert isinstance(result, str)
-
@pytest.mark.parametrize(
"file",
glob("tests/records/hakai-metadata-entry-form-files/**/*.yaml", recursive=True),
@@ -19,6 +19,6 @@ def test_xml(record):
def test_hakai_records_xml(file):
record = load(file, "yaml")
result = xml(record)
-
+
assert result
assert isinstance(result, str)