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
7 changes: 6 additions & 1 deletion .github/workflows/test-python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ on:
- 'src/**'
- 'tests/**'
- 'pyproject.toml'
- 'pixi.lock'
- '.github/workflows/test-python.yml'
pull_request:
paths:
- 'src/**'
- 'tests/**'
- 'pyproject.toml'
- 'pixi.lock'
- '.github/workflows/test-python.yml'
workflow_dispatch:

Expand All @@ -24,11 +26,14 @@ jobs:
uses: prefix-dev/setup-pixi@v0.10.0
with:
pixi-version: v0.67.2
locked: false
locked: true

- name: Check formatting with ruff
run: pixi run format-check

- name: Lint with ruff
run: pixi run lint

- name: Run tests
run: pixi run test

Expand Down
21 changes: 17 additions & 4 deletions PIXI.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,21 @@ pixi run test

## Split-repo development note

This repo is configured to consume `../earthscope-sfg-tools` as an editable local dependency via Pixi.
`earthscope-sfg-tools` and `pride-ppp` (from the GNSSommelier monorepo) are consumed as
git dependencies pinned to a tag/rev in `pyproject.toml` and resolved through `pixi.lock`
— no sibling checkout is required. When moving a pin, update every declaration and re-run
`pixi lock`:

If you clone this repository without the sibling `earthscope-sfg-tools` directory, update
`[tool.pixi.pypi-dependencies]` in `pyproject.toml` to use a published `earthscope-sfg-tools`
version instead.
- `earthscope-sfg-tools`: `[project.dependencies]` **and** `[tool.pixi.pypi-dependencies]`
- GNSSommelier: `pride-ppp` in `[project.dependencies]`, plus `gpm-specs` and
`gnss-product-management` in `[tool.pixi.pypi-dependencies]` — all three must share one
rev, or `pixi install --locked` (enforced in CI) rejects the lockfile.

To develop against a local checkout of either dependency, install it into the pixi env
over the locked version:

```bash
pixi run -- uv pip install --python "$(command -v python)" --no-deps --force-reinstall ../earthscope-sfg-tools
```

Restore the locked env afterwards with `rm -rf .pixi/envs && pixi install`.
126 changes: 63 additions & 63 deletions pixi.lock

Large diffs are not rendered by default.

12 changes: 9 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ authors = [
{ name = "Rachel Akie", email = "rachel.akie@earthscope.org" }
]
dependencies = [
"earthscope-sfg-tools[tiledb] @ git+https://github.com/EarthScope/earthscope-sfg-tools.git@0.2.0",
"earthscope-sfg-tools[tiledb] @ git+https://github.com/EarthScope/earthscope-sfg-tools.git@0.2.1",
"boto3>=1.40",
"botocore>=1.40",
"sqlalchemy>=2.0",
Expand All @@ -25,7 +25,7 @@ dependencies = [
"typer>=0.9",
"rich>=13.0",
"pyyaml>=6.0",
"pride-ppp @ git+https://github.com/EarthScope/GNSSommelier.git#subdirectory=packages/pride-ppp"
"pride-ppp @ git+https://github.com/EarthScope/GNSSommelier.git@b8db24e65d3a19577febb006373cf865f5e82bb8#subdirectory=packages/pride-ppp"
]

[project.optional-dependencies]
Expand Down Expand Up @@ -72,7 +72,13 @@ nodejs = ">=20"

[tool.pixi.pypi-dependencies]
earthscope_sfg_workflows = { path = ".", editable = true }
earthscope_sfg_tools = { git = "https://github.com/EarthScope/earthscope-sfg-tools.git", rev = "0.2.0", extras = ["tiledb"] }
earthscope_sfg_tools = { git = "https://github.com/EarthScope/earthscope-sfg-tools.git", rev = "0.2.1", extras = ["tiledb"] }
# gpm-specs and gnss-product-management are transitive deps of pride-ppp, but
# pride-ppp's own metadata requests them unpinned (default-branch HEAD), which
# fails `pixi install --locked` validation in CI. Pin them to the same
# GNSSommelier rev as pride-ppp; keep all three in sync when re-locking.
gpm-specs = { git = "https://github.com/EarthScope/GNSSommelier.git", subdirectory = "packages/gpm-specs", rev = "b8db24e65d3a19577febb006373cf865f5e82bb8" }
gnss-product-management = { git = "https://github.com/EarthScope/GNSSommelier.git", subdirectory = "packages/gnss-product-management", rev = "b8db24e65d3a19577febb006373cf865f5e82bb8" }

[tool.pixi.feature.tiledb.dependencies]
tiledb = ">=2.27"
Expand Down
1 change: 1 addition & 0 deletions src/earthscope_sfg_workflows/data_mgmt/adapters/memory.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from dataclasses import replace
from itertools import count
from pathlib import Path

from upath import UPath

from ..model import ArchiveFile, AssetEntry, AssetKind, SFGScope, FileInfo
Expand Down
1 change: 1 addition & 0 deletions src/earthscope_sfg_workflows/pipelines/qc_pipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,7 @@ def process_rinex(self) -> None:
processor = PrideProcessor(
pride_dir=pride_dir,
output_dir=intermediate_dir,
cli_config=pride_cfg.cli,
mode=ProcessingMode.DEFAULT,
)
rinex_path_map = {e.local_path: e for e in rinex_entries}
Expand Down
73 changes: 13 additions & 60 deletions src/earthscope_sfg_workflows/pipelines/sv3_pipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@
from pathlib import Path
from typing import Callable

# third-party — monkey-patch targets must be imported before the patches below
import tiledb as _tiledb
# third-party
from earthscope_sfg_tools import tiledb_integration as novb_ops
from earthscope_sfg_tools.novatel_tools.utils import get_metadata, get_metadatav2
from earthscope_sfg_tools.seafloor_site_tools.soundspeed_operations import (
Expand All @@ -29,7 +28,6 @@
rinex_qc,
tdb2rnx,
)
from earthscope_sfg_tools.tiledb_integration.arrays import TBDArray as _TBDArray
from earthscope_sfg_workflows.data_mgmt.ports import AssetCatalogPort
from earthscope_sfg_workflows.logging import ProcessLogger
from pride_ppp import (
Expand All @@ -38,8 +36,6 @@
kin_to_kin_position_df,
rinex_get_time_range,
)
from pride_ppp.factories.processor import PrideProcessor as _PrideProcessorCls
from pride_ppp.specifications.config import PRIDEPPPFileConfig as _PRIDEPPPFileConfig
from rich.progress import track

# local
Expand All @@ -64,59 +60,6 @@
)
from .shotdata_gnss_refinement import merge_shotdata_kinposition

# pride_ppp <= current version omits `ISB model` from generated config_files;
# pdp3 >= 3.2.7 requires it. Patch write_config_file to inject the line.
_pride_write_config_orig = _PRIDEPPPFileConfig.write_config_file


def _pride_write_config_patched(self, filepath):
_pride_write_config_orig(self, filepath)
p = Path(filepath)
text = p.read_text()
if "ISB model" not in text:
patched = []
for line in text.splitlines():
patched.append(line)
if line.startswith("RCK model"):
patched.append(
"ISB model = Default"
" ! GNSS receiver inter-system biases to be processed"
)
p.write_text("\n".join(patched) + "\n")


_PRIDEPPPFileConfig.write_config_file = _pride_write_config_patched


# pride_ppp _validate_kinfile uses `if kin_df` on a DataFrame — raises ValueError.
# Patch to use `is not None` check instead.
def _pride_validate_kinfile_patched(_self, kin_path, override=False):
if not override:
if not kin_path.exists():
return False
kin_df = kin_to_kin_position_df(kin_path)
if kin_df is not None and not kin_df.empty:
return True
return False


_PrideProcessorCls._validate_kinfile = _pride_validate_kinfile_patched


# TBDArray.write_df passes the DataFrame directly to tiledb.from_pandas, but
# tiledb requires the sparse dimension ('time') to be the pandas index, not a
# plain column. The DataFrame returned by kin_to_kin_position_df has time as
# a plain column. Patch write_df to set it as the index after validation.
def _tbd_write_df_patched(self, df, validate: bool = True):
if validate:
df = self.dataframe_schema.validate(df, lazy=True)
if "time" in df.columns:
df = df.set_index("time")
_tiledb.from_pandas(str(self.uri), df, mode="append")


_TBDArray.write_df = _tbd_write_df_patched


def _pipeline_method(fn):
"""Wrap a pipeline method so only one runs at a time per instance."""
Expand Down Expand Up @@ -327,12 +270,17 @@ def pre_process_novatel(self) -> None:
**merge_signature
):
try:
novb_ops.novatel_770_2tile(
result = novb_ops.novatel_770_2tile(
files=[x.local_path for x in novatel_770_entries],
gnss_obs_tdb=self.gnssObsTDBURI,
n_procs=self.config.novatel_config.n_processes,
logger=ProcessLogger.logger,
)
if result.returncode != 0:
raise RuntimeError(
f"novatel_770_2tile exited with code {result.returncode}: "
f"{result.stderr}"
)

self.catalog.add_merge_job(**merge_signature)
response = f"Added merge job for {len(novatel_770_entries)} Novatel 770 Entries to the catalog"
Expand Down Expand Up @@ -369,13 +317,17 @@ def pre_process_novatel(self) -> None:
**merge_signature
):
try:
novb_ops.nov0002tile(
result = novb_ops.nov0002tile(
files=[x.local_path for x in novatel_000_entries],
gnss_obs_tdb=self.gnssObsTDB_secondaryURI,
position_tdb=self.imuPositionTDB.uri,
n_procs=self.config.novatel_config.n_processes,
logger=ProcessLogger.logger,
)
if result.returncode != 0:
raise RuntimeError(
f"nov0002tile exited with code {result.returncode}: {result.stderr}"
)

self.catalog.add_merge_job(**merge_signature)
ProcessLogger.info(
Expand Down Expand Up @@ -830,6 +782,7 @@ def process_rinex(self) -> None:
processor = PrideProcessor(
pride_dir=pride_dir,
output_dir=intermediate_dir,
cli_config=pride_cfg.cli,
mode=ProcessingMode.DEFAULT,
)
rinex_path_map = {e.local_path: e for e in rinex_entries}
Expand Down
Loading
Loading