Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions docs/source/project_structure.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ oceanarray/
│ │ ├── helpers.py # Shared colormap/style helpers
│ │ └── _cli_legacy.py # Legacy CLI plots (pending `oceanarray plot <file>` redesign)
│ │
│ ├── report/ # [report] HTML report generation
│ ├── reports/ # [report] HTML report generation
│ │ ├── __init__.py # Public entry-points (MooringReport, etc.)
│ │ ├── _mooring.py # Mooring summary template + MooringReport orchestrator
│ │ ├── _instrument.py # Per-instrument report ({mooring}_{serial}_report.html)
Expand Down Expand Up @@ -172,7 +172,7 @@ Three-tier architecture (see `.claude/plotters_update-20260718.md` for the rules
- **Tier 1** (`plotters/primitives.py`): low-level axes primitives, no domain knowledge
- **Tier 2** (`plotters/current.py`, `timeseries.py`, `hydrography.py`, etc.): domain
functions that know about oceanographic variables
- **Tier 3** (`report/_plots.py`): report wrappers that call Tier-2 functions and return
- **Tier 3** (`reports/_plots.py`): report wrappers that call Tier-2 functions and return
base64 PNG strings for embedding in HTML

---
Expand All @@ -183,13 +183,13 @@ Report types, each in its own module:

| Report | Module | Output file |
|--------|--------|-------------|
| Mooring summary | `report/_mooring.py` | `{mooring}_report.html` |
| Per-instrument | `report/_instrument.py` | `{mooring}_{serial}_report.html` |
| Stack | `report/_stack.py` | `{mooring}_stack_report.html` |
| Grid | `report/_grid.py` | `{mooring}_grid_report.html` |
| Array | `report/_array.py` | `{array}_array_report.html` |
| Mooring summary | `reports/_mooring.py` | `{mooring}_report.html` |
| Per-instrument | `reports/_instrument.py` | `{mooring}_{serial}_report.html` |
| Stack | `reports/_stack.py` | `{mooring}_stack_report.html` |
| Grid | `reports/_grid.py` | `{mooring}_grid_report.html` |
| Array | `reports/_array.py` | `{array}_array_report.html` |

All figures are generated by `report/_plots.py` (Tier 3) and embedded as base64 PNGs.
All figures are generated by `reports/_plots.py` (Tier 3) and embedded as base64 PNGs.

---

Expand Down
8 changes: 4 additions & 4 deletions oceanarray/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ def cmd_report(args: argparse.Namespace) -> int:
return 0

if getattr(args, "array", False):
from .report._array import generate_array_report
from .reports._array import generate_array_report

if getattr(args, "pdf", False) or getattr(args, "all_reports", False):
_status(
Expand All @@ -366,7 +366,7 @@ def cmd_report(args: argparse.Namespace) -> int:
)
return 0 if result else 1

from .report import MooringReport
from .reports import MooringReport

_status("section", f"Report: {args.mooring}")
serials = getattr(args, "serial", None)
Expand All @@ -387,7 +387,7 @@ def cmd_report(args: argparse.Namespace) -> int:
stack=all_reports or args.stack,
)
if getattr(args, "cruise_table", False):
from .report._recovery_table import generate_recovery_table
from .reports._recovery_table import generate_recovery_table

mooring_proc = proc_root / args.mooring
out_dir = Path(
Expand All @@ -401,7 +401,7 @@ def cmd_report(args: argparse.Namespace) -> int:
force=args.force,
)
if getattr(args, "pdf", False) or all_reports:
from .report import combine_mooring_pdf
from .reports import combine_mooring_pdf

# Combine reads the same directory generate() wrote to; both resolve it
# through paths.resolve_report_dir so they can never drift.
Expand Down
2 changes: 1 addition & 1 deletion oceanarray/paths.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def resolve_report_dir(
"""Return the directory a mooring's HTML report pages are written to.

Single source of truth for report output-dir resolution, mirrored by both
:meth:`oceanarray.report.MooringReport.generate` and the PDF combiner so the
:meth:`oceanarray.reports.MooringReport.generate` and the PDF combiner so the
two never drift. Priority: explicit *outdir* wins; otherwise a central
*report_dir* nests each mooring under ``report_dir/<mooring>``; otherwise the
default ``proc_root/<mooring>/report``.
Expand Down
2 changes: 1 addition & 1 deletion oceanarray/plotters/current.py
Original file line number Diff line number Diff line change
Expand Up @@ -1506,7 +1506,7 @@ def draw_adcp_hodograph(
label_far = f"{float(range_vals[i_far]):.0f} m range"
label_near = f"{float(range_vals[i_near]):.0f} m range"

from oceanarray.report._plots import _draw_hodograph_pair
from oceanarray.reports._plots import _draw_hodograph_pair

fig, axes = plt.subplots(2, 2, figsize=(params.W_FULL, 9))
fig.subplots_adjust(hspace=0.55, wspace=0.45)
Expand Down
2 changes: 1 addition & 1 deletion oceanarray/plotters/timeseries.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ def draw_grid_velocity_stacked(ds: "xr.Dataset") -> "Optional[plt.Figure]":

"""
import matplotlib.pyplot as plt
from ..report._plots import _velocity_panel_style
from ..reports._plots import _velocity_panel_style

vel_vars = [
"east_velocity",
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion oceanarray/report/_pdf.py → oceanarray/reports/_pdf.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Combine a mooring's per-report HTML files into a single A4 PDF.

The HTML reports written by :class:`~oceanarray.report._mooring.MooringReport`
The HTML reports written by :class:`~oceanarray.reports._mooring.MooringReport`
are the single source of truth. This module post-processes those files with
WeasyPrint — it does not touch report generation or the Jinja templates. Print
layout (A4 page size, margins, page numbers, page-break avoidance, hidden nav
Expand Down
File renamed without changes.
File renamed without changes.
16 changes: 8 additions & 8 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -143,14 +143,14 @@ ignore = [
"oceanarray/tools/rapid_interp.py" = ["ANN", "D", "F841", "SLF001", "TRY003", "BLE001"] # legacy physics code; rewrite deferred
# Report generators: every function wraps rendering in try/except → None on failure.
# This is the established pattern for keeping HTML reports partially working on bad data.
"oceanarray/report/_plots.py" = ["BLE001", "TRY300", "TRY301", "TRY003"]
"oceanarray/reports/_plots.py" = ["BLE001", "TRY300", "TRY301", "TRY003"]
# Vendored byte-identical from ../ctdcast (shared report foundation, spec §9); kept
# identical for the planned cross-repo hash check, so lint exemptions live here rather
# than as edits that would fork the copy. Same try/except-to-None pattern as _plots.py.
"oceanarray/report/_encode.py" = ["TRY301", "TRY003", "D413"]
"oceanarray/report/_css.py" = ["D413"] # vendored; docstring section-blank-line style
"oceanarray/report/_html_helpers.py" = ["BLE001", "TRY300"]
"oceanarray/report/_grid.py" = ["BLE001", "TRY300"]
"oceanarray/report/_instrument.py" = ["BLE001", "TRY300", "ARG001"]
"oceanarray/report/_mooring.py" = ["BLE001", "TRY003", "TRY300"]
"oceanarray/report/_stack.py" = ["BLE001", "TRY300", "ARG001", "F841"]
"oceanarray/reports/_encode.py" = ["TRY301", "TRY003", "D413"]
"oceanarray/reports/_css.py" = ["D413"] # vendored; docstring section-blank-line style
"oceanarray/reports/_html_helpers.py" = ["BLE001", "TRY300"]
"oceanarray/reports/_grid.py" = ["BLE001", "TRY300"]
"oceanarray/reports/_instrument.py" = ["BLE001", "TRY300", "ARG001"]
"oceanarray/reports/_mooring.py" = ["BLE001", "TRY003", "TRY300"]
"oceanarray/reports/_stack.py" = ["BLE001", "TRY300", "ARG001", "F841"]
4 changes: 2 additions & 2 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@
def _raise_on_plot_error(monkeypatch):
"""Make figure-generation failures raise during tests instead of vanishing.

Flips :data:`oceanarray.report._plots.RAISE_ON_PLOT_ERROR` on for the
Flips :data:`oceanarray.reports._plots.RAISE_ON_PLOT_ERROR` on for the
duration of every test so a broken figure surfaces as a test failure rather
than a silently-absent panel. Reverted automatically by ``monkeypatch``.
"""
from oceanarray.report import _plots
from oceanarray.reports import _plots

monkeypatch.setattr(_plots, "RAISE_ON_PLOT_ERROR", True, raising=False)

Expand Down
2 changes: 1 addition & 1 deletion tests/integration/test_pipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ class TestInstrumentReport:

@pytest.fixture(autouse=True)
def run(self, proc_root_stage3_fresh):
from oceanarray.report import MooringReport
from oceanarray.reports import MooringReport

reporter = MooringReport(proc_dir=str(proc_root_stage3_fresh))
result = reporter.generate(
Expand Down
2 changes: 1 addition & 1 deletion tests/integration/test_recovery_table.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
missing-variable guards that only surface on a real NetCDF.
"""

from oceanarray.report._recovery_table import generate_recovery_table
from oceanarray.reports._recovery_table import generate_recovery_table

MOORING = "dune2_1_2026"

Expand Down
4 changes: 2 additions & 2 deletions tests/unit/test_array.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Unit tests for pure helper functions in oceanarray.report._array.
"""Unit tests for pure helper functions in oceanarray.reports._array.

Tests cover _parse_decdeg, _lat_lon_from_cfg, _count_instruments, and
_build_type_summary — all functions with no I/O dependencies that can be
Expand All @@ -7,7 +7,7 @@

import pytest

from oceanarray.report._array import (
from oceanarray.reports._array import (
_build_type_summary,
_count_instruments,
_lat_lon_from_cfg,
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/test_paths.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ def test_all_call_sites_agree():
"""
from oceanarray.processors.stage3 import _safe_serial as s3
from oceanarray.processors.helpers import _safe_serial as h
from oceanarray.report._html_helpers import _safe_serial as r
from oceanarray.reports._html_helpers import _safe_serial as r

raw = "16430, R01-024"
results = {safe_serial(raw), s3(raw), h(raw), r(raw)}
Expand Down
8 changes: 4 additions & 4 deletions tests/unit/test_pdf_report.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Unit tests for oceanarray/report/_pdf.py (HTML-to-PDF combination).
"""Unit tests for oceanarray/reports/_pdf.py (HTML-to-PDF combination).

`_ordered_report_files` is pure and tested without WeasyPrint; the render test
is gated behind an importorskip because WeasyPrint is an optional extra.
Expand All @@ -9,8 +9,8 @@
import pytest
import yaml

from oceanarray.report import MooringReport, combine_mooring_pdf
from oceanarray.report._pdf import _ordered_report_files
from oceanarray.reports import MooringReport, combine_mooring_pdf
from oceanarray.reports._pdf import _ordered_report_files

# Minimal mooring YAML that MooringReport.generate() can render a summary from
# (mirrors the fixture in test_report.py::TestMooringReport).
Expand Down Expand Up @@ -185,7 +185,7 @@ def test_all_pdf_best_effort_but_explicit_pdf_fails(
self, tmp_path: Path, monkeypatch
) -> None:
"""If the PDF can't be built: `--all` keeps HTML success (0); `--pdf` fails (1)."""
import oceanarray.report as report_pkg
import oceanarray.reports as report_pkg
from oceanarray.cli import build_parser

def _boom(*_a, **_k):
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/test_plot_guard.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import matplotlib.pyplot as plt
import pytest

from oceanarray.report import _plots
from oceanarray.reports import _plots


# ---------------------------------------------------------------------------
Expand Down
4 changes: 2 additions & 2 deletions tests/unit/test_recovery_table.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
"""Unit tests for the pure helpers in oceanarray.report._recovery_table.
"""Unit tests for the pure helpers in oceanarray.reports._recovery_table.

These are string/number formatters with no I/O — tested directly (the HTML
render path is exercised by the integration test).
"""

from oceanarray.report._recovery_table import (
from oceanarray.reports._recovery_table import (
_fmt_drift,
_instrument_label,
_interval_s,
Expand Down
20 changes: 10 additions & 10 deletions tests/unit/test_report.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Unit tests for oceanarray/report/ package.
"""Unit tests for oceanarray/reports/ package.

Three test classes:
TestHtmlHelpers — pure functions in _html_helpers.py
Expand All @@ -13,7 +13,7 @@
import yaml
from pathlib import Path

from oceanarray.report._html_helpers import (
from oceanarray.reports._html_helpers import (
_duration_str,
_fmt_dt,
_fmt_minmax,
Expand All @@ -26,7 +26,7 @@
_safe_serial,
_stage_files,
)
from oceanarray.report._mooring import MooringReport
from oceanarray.reports._mooring import MooringReport


# ---------------------------------------------------------------------------
Expand Down Expand Up @@ -461,7 +461,7 @@ def mooring_setup(self, tmp_path):

# generate_instrument_pages
def test_generate_instrument_pages_creates_html(self, mooring_setup):
from oceanarray.report._instrument import generate_instrument_pages
from oceanarray.reports._instrument import generate_instrument_pages

setup = mooring_setup
report = MooringReport(proc_dir=str(setup["proc_dir"].parent))
Expand All @@ -484,7 +484,7 @@ def test_generate_instrument_pages_creates_html(self, mooring_setup):
assert len(pages) >= 1

def test_generate_instrument_pages_html_contains_serial(self, mooring_setup):
from oceanarray.report._instrument import generate_instrument_pages
from oceanarray.reports._instrument import generate_instrument_pages

setup = mooring_setup
report = MooringReport(proc_dir=str(setup["proc_dir"].parent))
Expand All @@ -508,7 +508,7 @@ def test_generate_instrument_pages_html_contains_serial(self, mooring_setup):

def test_generate_instrument_pages_no_nc(self, tmp_path):
"""Should complete without error even when no NC files exist."""
from oceanarray.report._instrument import generate_instrument_pages
from oceanarray.reports._instrument import generate_instrument_pages

proc_dir = tmp_path / "proc" / "TEST_M1"
proc_dir.mkdir(parents=True)
Expand Down Expand Up @@ -548,7 +548,7 @@ def test_generate_instrument_pages_no_nc(self, tmp_path):

# generate_stack_page
def test_generate_stack_page_creates_html(self, mooring_setup):
from oceanarray.report._stack import generate_stack_page
from oceanarray.reports._stack import generate_stack_page

setup = mooring_setup
stack_nc = setup["proc_dir"] / "TEST_M1_stack.nc"
Expand All @@ -569,7 +569,7 @@ def test_generate_stack_page_creates_html(self, mooring_setup):
assert out.stat().st_size > 0

def test_generate_stack_page_html_structure(self, mooring_setup):
from oceanarray.report._stack import generate_stack_page
from oceanarray.reports._stack import generate_stack_page

setup = mooring_setup
stack_nc = setup["proc_dir"] / "TEST_M1_stack.nc"
Expand All @@ -592,7 +592,7 @@ def test_generate_stack_page_html_structure(self, mooring_setup):

# generate_grid_page
def test_generate_grid_page_creates_html(self, mooring_setup):
from oceanarray.report._grid import generate_grid_page
from oceanarray.reports._grid import generate_grid_page

setup = mooring_setup
grid_nc = setup["proc_dir"] / "TEST_M1_grid.nc"
Expand All @@ -613,7 +613,7 @@ def test_generate_grid_page_creates_html(self, mooring_setup):
assert out.stat().st_size > 0

def test_generate_grid_page_html_contains_mooring(self, mooring_setup):
from oceanarray.report._grid import generate_grid_page
from oceanarray.reports._grid import generate_grid_page

setup = mooring_setup
grid_nc = setup["proc_dir"] / "TEST_M1_grid.nc"
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/test_report_golden.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

import pytest

from oceanarray.report import MooringReport
from oceanarray.reports import MooringReport
from oceanarray.utilities import _safe_rel

_FIXTURES = pathlib.Path(__file__).resolve().parents[1] / "fixtures"
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/test_report_tokens.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import pytest

from oceanarray.config import report_tokens as tok
from oceanarray.report import _encode
from oceanarray.reports import _encode


# ---------------------------------------------------------------------------
Expand Down
Loading