diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
new file mode 100644
index 0000000..6a67b52
--- /dev/null
+++ b/.devcontainer/devcontainer.json
@@ -0,0 +1,32 @@
+// For format details, see https://aka.ms/devcontainer.json. For config options, see the
+// README at: https://github.com/devcontainers/templates/tree/main/src/docker-existing-dockerfile
+{
+ "name": "Existing Dockerfile",
+ "build": {
+ // Sets the run context to one level up instead of the .devcontainer folder.
+ "context": "..",
+ // Update the 'dockerFile' property if you aren't using the standard 'Dockerfile' filename.
+ "dockerfile": "../Dockerfile"
+ },
+
+ // Features to add to the dev container. More info: https://containers.dev/features.
+ // "features": {},
+
+ // Use 'forwardPorts' to make a list of ports inside the container available locally.
+ // "forwardPorts": [],
+
+ // Uncomment the next line to run commands after the container is created.
+ "postCreateCommand": "python3 -m pip install -e .[test]",
+
+ // Configure tool-specific properties.
+ "customizations": {
+ "vscode": {
+ "extensions": [
+ "ms-python.python"
+ ]
+ }
+ }
+
+ // Uncomment to connect as an existing user other than the container default. More info: https://aka.ms/dev-containers-non-root.
+ // "remoteUser": "devcontainer"
+}
diff --git a/.devcontainer/docker-compose.yaml b/.devcontainer/docker-compose.yaml
new file mode 100644
index 0000000..1b9cf2f
--- /dev/null
+++ b/.devcontainer/docker-compose.yaml
@@ -0,0 +1,26 @@
+# See https://github.com/devcontainers/templates/blob/main/src/docker-existing-docker-compose/.devcontainer/docker-compose.yml
+version: '3.8'
+services:
+ # Update this to the name of the service you want to work with in your docker-compose.yml file
+ tox:
+ # Uncomment if you want to override the service's Dockerfile to one in the .devcontainer
+ # folder. Note that the path of the Dockerfile and context is relative to the *primary*
+ # docker-compose.yml file (the first in the devcontainer.json "dockerComposeFile"
+ # array). The sample below assumes your primary file is in the root of your project.
+ #
+ # build:
+ # context: .
+ # dockerfile: .devcontainer/Dockerfile
+
+ volumes:
+ # Update this to wherever you want VS Code to mount the folder of your project
+ - ..:/workspaces:cached
+
+ # Uncomment the next four lines if you will use a ptrace-based debugger like C++, Go, and Rust.
+ # cap_add:
+ # - SYS_PTRACE
+ # security_opt:
+ # - seccomp:unconfined
+
+ # Overrides default command so things don't shut down after the process ends.
+ command: sleep infinity
diff --git a/Dockerfile b/Dockerfile
index ff2f0ee..eba06c7 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -21,4 +21,4 @@ RUN : \
WORKDIR /hls_vi
COPY ./ ./
-CMD ["tox", "-r", "-v"]
+ENTRYPOINT [ "tox" ]
diff --git a/Makefile b/Makefile
index 12123f6..315cbaa 100644
--- a/Makefile
+++ b/Makefile
@@ -5,4 +5,7 @@ build:
docker compose build
test:
- docker compose run --rm --build tox
+ docker compose run --rm --build tox -- -v
+
+test-metadata:
+ docker compose run --rm --build tox -- -v -k "not test_generate_indices"
diff --git a/README.md b/README.md
index 22691bc..43a37a5 100644
--- a/README.md
+++ b/README.md
@@ -38,5 +38,6 @@ where:
You can run tests using Docker:
```bash
-make test
+make test # Run all tests
+make test-metadata # Run only CMR and STAC metadata tests
```
diff --git a/hls_vi/generate_metadata.py b/hls_vi/generate_metadata.py
index 7fdbce9..1bd6681 100644
--- a/hls_vi/generate_metadata.py
+++ b/hls_vi/generate_metadata.py
@@ -6,7 +6,7 @@
from datetime import datetime, timezone
from pathlib import Path
-from typing import List, Optional, Tuple
+from typing import List, Tuple
import rasterio
from lxml import etree as ET
@@ -83,7 +83,7 @@ def generate_metadata(input_dir: Path, output_dir: Path) -> None:
to this directory with the name `HLS-VI.*.cmr.xml`.
"""
metadata_path = next(input_dir.glob("HLS.*.cmr.xml"))
- tree = ET.parse(str(metadata_path))
+ tree = ET.parse(str(metadata_path), None)
with rasterio.open(next(output_dir.glob("*.tif"))) as vi_tif:
tags = vi_tif.tags()
@@ -123,7 +123,13 @@ def generate_metadata(input_dir: Path, output_dir: Path) -> None:
tree.find("Temporal/RangeDateTime/BeginningDateTime").text = sensing_time_begin
tree.find("Temporal/RangeDateTime/EndingDateTime").text = sensing_time_end
- with (importlib_resources.files("hls_vi") / "schema" / "Granule.xsd").open() as xsd:
+ tree.find("DataFormat").text = "COG"
+
+ with (
+ importlib_resources.files("hls_vi")
+ / "schema"
+ / "Granule.xsd" # pyright: ignore[reportOperatorIssue]
+ ).open() as xsd:
ET.XMLSchema(file=xsd).assertValid(tree)
tree.write(
@@ -152,21 +158,21 @@ def normalize_additional_attributes(container: ElementBase) -> None:
around the `" + "` and (arbitrarily) using the first value as the value of the
additional attribute.
"""
- attrs: List[ElementBase] = container.findall("./AdditionalAttribute", None)
-
- for attr in attrs:
- value_element: Optional[ElementBase] = attr.find(".//Value", None)
- value_text: str = value_element.text if value_element is not None else ""
-
- if value_element is not None:
- # Replace the text of the additional attribute with the first value
- # obtained by splitting the text on " + ". If the text does not contain
- # " + ", the text remains the same. For example, "05.11".split(" + ") is
- # simply ["05.11"], so taking the first element simply produces "05.11".
- normalized = value_text.split(" + ", 1)[0].strip()
- value_element.text = (
- normalized # pyright: ignore[reportAttributeAccessIssue]
- )
+ attr_els: List[ElementBase] = container.findall("./AdditionalAttribute", None)
+
+ for attr_el in attr_els:
+ normalize_additional_attribute(attr_el)
+
+
+def normalize_additional_attribute(attr_el: ElementBase) -> None:
+ values_el: ElementBase = attr_el.find("./Values", None)
+
+ for el in iter(values_el):
+ # Replace the text of the additional attribute value with the first value
+ # obtained by splitting the text on " + ". If the text does not contain
+ # " + ", the text remains the same. For example, "05.11".split(" + ") is
+ # simply ["05.11"], so taking the first element simply produces "05.11".
+ el.text = el.text.split(" + ", 1)[0].strip()
def set_additional_attribute(attrs: ElementBase, name: str, value: str) -> None:
diff --git a/setup.py b/setup.py
index 969124d..713b4ed 100644
--- a/setup.py
+++ b/setup.py
@@ -19,7 +19,7 @@
],
extras_require={
"test": [
- "black[jupyter]==21.12b0",
+ "black[jupyter]==22.8.0", # Last version to support Python 3.6 runtime
"flake8",
"mypy",
"pytest",
diff --git a/tests/fixtures/HLS-VI.L30.T06WVS.2024120T211159.v2.0.cmr.xml b/tests/fixtures/HLS-VI.L30.T06WVS.2024120T211159.v2.0.cmr.xml
index 77fbc45..01ba39b 100644
--- a/tests/fixtures/HLS-VI.L30.T06WVS.2024120T211159.v2.0.cmr.xml
+++ b/tests/fixtures/HLS-VI.L30.T06WVS.2024120T211159.v2.0.cmr.xml
@@ -245,7 +245,7 @@
- Cloud Optimized GeoTIFF (COG)
+ COG
diff --git a/tests/fixtures/HLS-VI.S30.T13RCN.2024128T173909.v2.0.cmr.xml b/tests/fixtures/HLS-VI.S30.T13RCN.2024128T173909.v2.0.cmr.xml
index e9334b3..9211d51 100644
--- a/tests/fixtures/HLS-VI.S30.T13RCN.2024128T173909.v2.0.cmr.xml
+++ b/tests/fixtures/HLS-VI.S30.T13RCN.2024128T173909.v2.0.cmr.xml
@@ -307,7 +307,7 @@
- Cloud Optimized GeoTIFF (COG)
+ COG
diff --git a/tox.ini b/tox.ini
index 0652a76..fe79ee2 100644
--- a/tox.ini
+++ b/tox.ini
@@ -13,7 +13,6 @@ max-complexity = 12
max-line-length = 90
[testenv]
-basepython = python3.6
envdir = venv
sitepackages = True
extras =
@@ -21,4 +20,4 @@ extras =
commands =
flake8
mypy
- pytest -vv --doctest-modules
+ pytest -vv --doctest-modules {posargs}