diff --git a/.vscode/launch.json b/.vscode/launch.json
index 2d93b6c..574a766 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -467,9 +467,9 @@
// Test --update_ssds_provenance with a short log_file
//"args": ["-v", "1", "--log_file", "ahi/missionlogs/2025/20250128_20250131/20250131T051404/202501310514_202501310535.nc4", "--update_ssds_provenance"]
// Test ahi mission that has Backseat Planktivore data with --update_ssds_provenance
- //"args": ["-v", "1", "--log_file", "ahi/missionlogs/2025/20250414_20250418/20250415T040019/202504150400_202504152346.nc4", "--update_ssds_provenance"]
+ "args": ["-v", "1", "--log_file", "ahi/missionlogs/2025/20250414_20250418/20250415T040019/202504150400_202504152346.nc4", "--update_ssds_provenance"]
// Make per log file .html files to test with lrauv_deployment_plots and --update_ssds_provenance
- "args": ["-v", "1", "--auv_name", "ahi", "--start", "20251022T000000", "--end", "20251024T000000", "--update_ssds_provenance", "--clobber"]
+ //"args": ["-v", "1", "--auv_name", "ahi", "--start", "20251022T000000", "--end", "20251024T000000", "--update_ssds_provenance", "--clobber"]
},
{
"name": "lrauv_deployment_plots",
@@ -484,13 +484,13 @@
// ahi planktivore deployment April 2025 add --update_ssds_provenance
//"args": ["-v", "1", "--dlist", "ahi/missionlogs/2025/20250414_20250418.dlist", "--update_ssds_provenance"]
// Test time range of DeploymentPlots with ahi planktivore deployment April 2025
- //"args": ["-v", "1", "--auv_name", "ahi", "--start", "20251001", "--end", "20251231", "--update_ssds_provenance", "--force"]
+ "args": ["-v", "1", "--auv_name", "ahi", "--start", "20250401", "--end", "20250501", "--update_ssds_provenance", "--force"]
// Test web page building with a short deployment
//"args": ["-v", "1", "--dlist", "ahi/missionlogs/2025/20251022_20251024.dlist", "--update_ssds_provenance"]
// Test --force option for rebuilding web pages with a short deployment
//"args": ["-v", "1", "--last_n_days", "10", "--update_ssds_provenance", "--force"]
// Test --notify option
- "args": ["-v", "1", "--dlist", "ahi/missionlogs/2025/20251022_20251024.dlist", "--update_ssds_provenance", "--force", "--notify", "mccann@mbari.org"]
+ //"args": ["-v", "1", "--dlist", "ahi/missionlogs/2025/20251022_20251024.dlist", "--update_ssds_provenance", "--force", "--notify", "mccann@mbari.org"]
},
diff --git a/src/data/create_products.py b/src/data/create_products.py
index 253046a..55b5345 100755
--- a/src/data/create_products.py
+++ b/src/data/create_products.py
@@ -2030,6 +2030,7 @@ def plot_2column(self) -> str: # noqa: C901, PLR0912, PLR0915
if self.output_dir is not None
else Path(BASE_LRAUV_PATH, f"{Path(self.log_file).parent}")
)
+ out_dir.mkdir(parents=True, exist_ok=True)
stem = (
self.plot_name_stem if self.plot_name_stem is not None else Path(self.log_file).stem
)
@@ -2170,6 +2171,7 @@ def plot_biolume_2column(self) -> str: # noqa: C901, PLR0912, PLR0915
if self.output_dir is not None
else Path(BASE_LRAUV_PATH, f"{Path(self.log_file).parent}")
)
+ out_dir.mkdir(parents=True, exist_ok=True)
stem = (
self.plot_name_stem if self.plot_name_stem is not None else Path(self.log_file).stem
)
@@ -2311,6 +2313,7 @@ def plot_planktivore_2column(self) -> str: # noqa: C901, PLR0912, PLR0915
if self.output_dir is not None
else Path(BASE_LRAUV_PATH, f"{Path(self.log_file).parent}")
)
+ out_dir.mkdir(parents=True, exist_ok=True)
stem = (
self.plot_name_stem if self.plot_name_stem is not None else Path(self.log_file).stem
)
diff --git a/src/data/lrauv_deployment_plots.py b/src/data/lrauv_deployment_plots.py
index b54e1bf..68b9a55 100755
--- a/src/data/lrauv_deployment_plots.py
+++ b/src/data/lrauv_deployment_plots.py
@@ -247,7 +247,7 @@ def plot_deployment( # noqa: C901, PLR0912, PLR0913, PLR0915
# Deployment name (spaces → underscores for filenames)
raw_name = self._parse_deployment_name(dlist_content) if dlist_content else None
if raw_name:
- plot_name_stem = raw_name.replace(" ", "_")
+ plot_name_stem = raw_name.replace(" ", "_").replace("/", "_")
self.logger.info("Deployment name: %s", raw_name)
else:
plot_name_stem = dlist_rel.stem
@@ -367,6 +367,7 @@ def _build_and_write_html( # noqa: PLR0913
nc_files,
auv_name=_auv_name,
png_file_path=Path(png_path),
+ other_png_paths=[p for p in png_paths if p != png_path],
)
self.logger.info("Per-PNG HTML written to %s", per_png_html)
archiver = Archiver(add_handlers=True, clobber=True)
@@ -527,16 +528,6 @@ def _stoqs_url_for_nc_url(self, nc_url: str, auv_name: str) -> str | None:
self.logger.debug("Could not generate per-log STOQS URL for %s: %s", nc_url, exc)
return None
- def _per_log_html_url(self, nc_urls: list[str]) -> str:
- """Return the first reachable per-log HTML URL for a list of nc URLs, or empty string."""
- for nc_url in nc_urls:
- candidate = nc_url.replace(
- LRAUV_OPENDAP_BASE.rstrip("/"), BASE_LRAUV_WEB.rstrip("/")
- ).replace(f"_{FREQ}.nc", f"_{FREQ}.html")
- if self._url_exists(candidate):
- return candidate
- return ""
-
def _per_log_stoqs_url(
self, nc_urls: list[str], auv_name: str, fallback: str | None
) -> str | None:
@@ -548,15 +539,15 @@ def _per_log_stoqs_url(
return url
return fallback
- def _per_log_png_links(self, nc_urls: list[str]) -> str:
- """Return HTML anchor tags for each existing per-log PNG, pipe-separated."""
- parts: list[str] = []
+ def _per_log_png_links(self, nc_urls: list[str]) -> list[tuple[str, str]]:
+ """Return (url, label) pairs for each existing per-log PNG."""
+ parts: list[tuple[str, str]] = []
for nc_url in nc_urls:
for png_url in self._png_urls_for_nc(nc_url):
if self._url_exists(png_url):
pname = png_url.rsplit("/", 1)[1]
- parts.append(f'{pname}')
- return " | ".join(parts)
+ parts.append((png_url, pname))
+ return parts
def _write_per_png_html( # noqa: C901, PLR0913
self,
@@ -568,6 +559,7 @@ def _write_per_png_html( # noqa: C901, PLR0913
nc_files: list[str],
auv_name: str = "",
png_file_path: Path | None = None,
+ other_png_paths: list[str] | None = None,
) -> None:
"""Write a plain HTML page for one deployment PNG.
@@ -581,37 +573,59 @@ def _write_per_png_html( # noqa: C901, PLR0913
log_dir = url.rsplit("/", 2)[1]
grouped.setdefault(log_dir, []).append(url)
- log_items = ""
+ # Collect per-row data first so we can suppress empty columns
+ rows: list[dict] = []
for log_dir in sorted(grouped):
nc_urls = grouped[log_dir]
- per_log_html_url = self._per_log_html_url(nc_urls)
log_stoqs_url = self._per_log_stoqs_url(nc_urls, auv_name, stoqs_url)
-
- links = ""
- if per_log_html_url:
- links += f' image'
png_links = self._per_log_png_links(nc_urls)
- if png_links:
- if links:
- links += " | "
- links += png_links
- for nc_url in nc_urls:
- dap_form_url = nc_url + ".html"
- if links:
- links += " | "
- links += f'OPeNDAP Data Access Form'
- if log_stoqs_url:
- if links:
- links += " | "
- links += f'STOQS'
+ rows.append(
+ {
+ "dir": log_dir,
+ "plots": "
".join(f'{lbl}' for u, lbl in png_links),
+ "dap": "".join(f'OPeNDAP' for nc_url in nc_urls),
+ "stoqs": f'STOQS' if log_stoqs_url else "",
+ }
+ )
- log_items += f"
Other plots for this deployment: " + " | ".join(sibling_links) + "
\n" + ) stoqs_line = "" if stoqs_url: after_scheme = stoqs_url.split("//", 1)[-1] if "//" in stoqs_url else stoqs_url db_label = after_scheme.split("/")[1] if "/" in after_scheme else after_scheme - stoqs_line = f'\n' + stoqs_line = f'View these data in {db_label}
\n' if png_file_path is not None and png_file_path.exists(): b64 = base64.b64encode(png_file_path.read_bytes()).decode("ascii") @@ -638,11 +652,10 @@ def _write_per_png_html( # noqa: C901, PLR0913 "\n" f"