Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
39111f1
move some vector plotting functionality into vector_utils.py (#2321)
megannissel Feb 18, 2026
a92d069
groundwork for a swy report (#2321)
megannissel Feb 18, 2026
a257cba
Merge branch 'release/3.18.0' into feature/2321-swy-report
megannissel Feb 20, 2026
03ef51f
second iteration of swy report (#2321)
megannissel Mar 3, 2026
e3eefbc
Merge branch 'main' into feature/2321-swy-report
megannissel Mar 3, 2026
eb61c0d
interactive baseflow and quickflow plots, now in m3/s (#2321)
megannissel Mar 5, 2026
6e1bd1d
adjust raster_utils.raster_inputs_summary to check input csvs for ras…
megannissel Mar 6, 2026
4afb5e1
reorganize report sections; include monthly qf rasters alongside annu…
megannissel Mar 6, 2026
fc6b23b
fix qf+b graph to show sums when multiple features are selected (#2321)
megannissel Mar 9, 2026
a56e945
move chart_landmass back into CV reporter (not used elsewhere) (#2321)
megannissel Mar 11, 2026
d779405
fix alignment of monthly tickmarks; add FID to aggregate_vector.shp r…
megannissel Mar 11, 2026
aa4dfb4
move construction of monthly qf & b csv into execute (#2321)
megannissel Mar 12, 2026
70b252e
update SWY created_if conditions; update reporter conditional logic t…
megannissel Mar 12, 2026
f90e7d5
mask qf rasters by stream network for improved contrast (#2321)
megannissel Mar 13, 2026
faa6ed0
fix quotes in f-string (#2321)
megannissel Mar 13, 2026
b7e061c
Revert "mask qf rasters by stream network for improved contrast (#2321)"
megannissel Mar 13, 2026
cbfe933
log-transform qf plots for increased contrast; add small plots and cu…
megannissel Mar 16, 2026
169be1e
summarize output raster stats for nested paths; expand utils.fake_exe…
megannissel Mar 18, 2026
18047bc
minor edits to swy reporter, including supertitle for raster facet gr…
megannissel Mar 18, 2026
3b3b0f5
update SWY outputs units in model_spec (#2321)
megannissel Mar 18, 2026
d096609
basic testing for swy reporter template (#2321)
megannissel Mar 18, 2026
26753e3
always pass model spec to `raster_inputs_summary` (#2321)
megannissel Mar 18, 2026
cecba2e
swy report template testing (#2321)
megannissel Mar 19, 2026
375e96c
accept either str or colormap object for RasterPlotConfig.colormap; u…
megannissel Mar 19, 2026
7e51bdd
improvements to SWY reporter, including better axis and legend config…
megannissel Mar 19, 2026
93f9c73
document geom_id in aggregate vector output spec; adjust gemon_id des…
megannissel Mar 19, 2026
8c45a1a
update SWY model to index all monthly outputs consistently by month, …
megannissel Mar 19, 2026
4c4a789
reduce tests checking quickflow/baseflow CSV values to the base regre…
megannissel Mar 19, 2026
5e78639
snapshot tests for plot_raster_facets small plots (#2321)
megannissel Mar 20, 2026
68f4641
raster_workspace_summary: recursively handle nested file_registry (#2…
megannissel Mar 20, 2026
3903ad4
provide quickflow, baseflow, and precip CSV values in m3/month (#2321)
megannissel Mar 20, 2026
47b69cb
update qf+b+precip chart axis labels to reflect m3/month (#2321)
megannissel Mar 20, 2026
2b2aff7
Split make deploy into artifact specific recipes #831
claire-simpson Mar 20, 2026
0ac1dc4
Use new make targets in GHA build and test workflow #831
claire-simpson Mar 20, 2026
6645ee4
update MONTH_ID_TO_LABEL so that it is indexed in line with month num…
megannissel Mar 20, 2026
721ab80
Keep deploy target unchanged #831
claire-simpson Mar 20, 2026
7150fca
add regression test for user_defined_climate_zones base case (#2321)
megannissel Mar 20, 2026
24b9f96
fix test docstring (#2321)
megannissel Mar 20, 2026
cbdee5b
Added history note #831
claire-simpson Mar 20, 2026
779f84f
fixes and improvements to utils; different color scheme for qb, vri_s…
megannissel Mar 20, 2026
c3a7f95
custom matplotlib colormap for local recharge raster that matches the…
megannissel Mar 20, 2026
30c8535
use built-in matplotlib BrBG instead of custom version (#2321)
megannissel Mar 23, 2026
b86deab
update HISTORY.rst to reflect updates to the SWY model and report (#2…
megannissel Mar 23, 2026
25bdb43
Merge pull request #2439 from megannissel/feature/2321-swy-report
davemfish Mar 23, 2026
88b796a
Merge pull request #2448 from claire-simpson/bugfix/831-make-deploy-e…
dcdenu4 Mar 23, 2026
e25de0c
Merge pull request #2453 from natcap/feature/swy-report
davemfish Mar 24, 2026
6992ea8
Updating Makefile to make it easier to replicate.
phargogh Mar 24, 2026
69b038d
Always removing the signed file.
phargogh Mar 24, 2026
11ebd38
Running the service as root.
phargogh Mar 24, 2026
a19afde
Adding some more packages.
phargogh Mar 24, 2026
abfdcdf
remove continue-on-error from puppeteer test, but run the test after …
davemfish Mar 25, 2026
2fdc5fd
Merge pull request #2456 from phargogh/bugfix/2455-codesigning-failing
megannissel Mar 25, 2026
d2215c2
switch QF colormap to Blues; use month indices instead of names; add …
megannissel Mar 24, 2026
bfbd456
Merge pull request #2458 from natcap/autopr/main-rev2fdc5fd-into-rele…
phargogh Mar 25, 2026
310ed08
Merge pull request #2457 from davemfish/bugfix/GHA-2454-pptr-test-con…
davemfish Mar 26, 2026
a468731
Merge pull request #2461 from megannissel/feature/2321-swy-report-upd…
megannissel Mar 26, 2026
8089c67
Merge pull request #2465 from natcap/autopr/main-rev310ed08-into-rele…
davemfish Apr 1, 2026
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
35 changes: 17 additions & 18 deletions .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ jobs:
if: github.event_name != 'pull_request'
env:
CLOUDSDK_PYTHON: ${{ steps.python-gsutil-task.outputs.python-path }}
run: make deploy
run: make deploy_dist

test-source-distribution:
name: Check sdist
Expand Down Expand Up @@ -270,7 +270,7 @@ jobs:
if: github.event_name != 'pull_request' && matrix.os == 'macos-15-intel' && matrix.python-version == env.LATEST_SUPPORTED_PYTHON_VERSION
env:
CLOUDSDK_PYTHON: ${{ steps.python-gsutil-task.outputs.python-path }}
run: make deploy
run: make deploy_dist

validate-resources:
name: Validate Sampledata & User Guide
Expand Down Expand Up @@ -461,23 +461,18 @@ jobs:
yarn run build
yarn run dist

- name: Test electron app with puppeteer
continue-on-error: true # test is flaky on GHA
working-directory: workbench
run: npx cross-env CI=true yarn run test-electron-app

# gsutil (part of make deploy) never seems to support the latest python version.
- name: Set up python for gsutil
uses: actions/setup-python@v6
with:
python-version: ${{ env.GSUTIL_PYTHON_VERSION }}
id: python-gsutil-task

- name: Deploy artifacts to GCS
- name: Deploy binaries and user guide to GCS
if: github.event_name != 'pull_request'
env:
CLOUDSDK_PYTHON: ${{ steps.python-gsutil-task.outputs.python-path }}
run: make deploy
run: make deploy_dist deploy_userguide deploy_workbench

# This relies on the file existing on GCP, so it must be run after `make
# deploy` is called.
Expand All @@ -502,13 +497,6 @@ jobs:
name: InVEST-user-guide
path: dist/InVEST_*_userguide.zip

- name: Upload workbench logging from puppeteer
uses: actions/upload-artifact@v4
if: always()
with:
name: ${{ runner.os }}_puppeteer_log.zip'
path: ${{ matrix.puppeteer-log }}

- name: Run invest-autotest with binaries
id: invest-autotest
if : |
Expand All @@ -532,6 +520,17 @@ jobs:
DEST=gs://releases.naturalcapitalproject.org/invest-reports/latest
gsutil -m rsync -r ${{ steps.invest-autotest-deploy.outputs.REPORTS_BASE_URL }} $DEST

- name: Test electron app with puppeteer
working-directory: workbench
run: npx cross-env CI=true yarn run test-electron-app

- name: Upload workbench logging from puppeteer
uses: actions/upload-artifact@v4
if: always()
with:
name: ${{ runner.os }}_puppeteer_log.zip'
path: ${{ matrix.puppeteer-log }}

- name: Tar the workspace to preserve permissions
if: failure()
run: tar -cvf ${{ matrix.workspace-path}} ${{ github.workspace }}
Expand Down Expand Up @@ -579,8 +578,8 @@ jobs:
if: github.event_name != 'pull_request'
uses: google-github-actions/setup-gcloud@v3

- name: Deploy artifacts to GCS
- name: Deploy sample data to GCS
if: github.event_name != 'pull_request'
env:
CLOUDSDK_PYTHON: ${{ steps.python-gsutil-task.outputs.python-path }}
run: make deploy
run: make deploy_data
32 changes: 29 additions & 3 deletions HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,36 @@
7. InVEST model Z (model names should be sorted A-Z)


Unreleased Changes
------------------

Seasonal Water Yield
====================
* The model now generates a report, a visual summary of results, available in
the output workspace and also viewable from the Workbench after the model run
completes. (`#2321 <https://github.com/natcap/invest/issues/2321>`_)
* The model now generates an additional output, a CSV containing average monthly
quickflow, baseflow, and precipitation values, in cubic meters per month, for
each feature in the AOI. This output is used by the report to generate some
plots. Note that this CSV is only created when the model is run without
inputting a Local Recharge raster.
(`#2321 <https://github.com/natcap/invest/issues/2321>`_)
* Various updates to model output data metadata, including correcting the
units of some outputs.
(`#2450 <https://github.com/natcap/invest/issues/2450>`_)
* Updated the naming convention of several monthly intermediate outputs to be
1-indexed rather than 0-indexed. This makes filenames consistent throughout
the model, where 1=January and 12=December.
(`#2451 <https://github.com/natcap/invest/issues/2451>`_)


General
=======
* Refactored ``make deploy`` into artifact-specific targets (``deploy_dist``,
``deploy_data``, ``deploy_userguide``, ``deploy_workbench``) and updated
these targets to fail on missing artifacts instead of silently ignoring
errors. (`#831 <https://github.com/natcap/invest/issues/813>`_)

..
Unreleased Changes
------------------

3.18.0 (2026-02-25)
-------------------
Expand Down
21 changes: 19 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ GIT_SAMPLE_DATA_REPO_REV := cfd1f07673e66823fd22989a2b87afb017aac447

GIT_TEST_DATA_REPO := https://bitbucket.org/natcap/invest-test-data.git
GIT_TEST_DATA_REPO_PATH := $(DATA_DIR)/invest-test-data
GIT_TEST_DATA_REPO_REV := c791f2b50e67680832054536899efacbc72e9e0b
GIT_TEST_DATA_REPO_REV := 94c4bc9f0f22082d2251b6b14063eb0ff4094451

GIT_UG_REPO := https://github.com/natcap/invest.users-guide
GIT_UG_REPO_PATH := doc/users-guide
Expand Down Expand Up @@ -124,7 +124,8 @@ INVEST_AUTOTESTER := $(PYTHON) scripts/invest-autotest.py --cwd $(GIT_SAMPLE_DAT


.PHONY: fetch install binaries apidocs userguide changelog sampledata \
sampledata_single test clean help check python_packages purge deploy codesign \
sampledata_single test clean help check python_packages purge deploy \
deploy_dist deploy_data deploy_userguide deploy_workbench codesign \
validate_sampledata validate_userguide_filenames invest_autotest deploy_autotest_reports \
$(GIT_SAMPLE_DATA_REPO_PATH) $(GIT_TEST_DATA_REPO_PATH) $(GIT_UG_REPO_REV)

Expand Down Expand Up @@ -159,6 +160,10 @@ help:
@echo " invest_autotest to run invest via the CLI on all sampledata datastacks"
@echo " deploy_autotest_reports to deploy to GCS any model's HTML report generated by the autotest"
@echo " deploy to deploy any artifacts in dist/ folder to GCS"
@echo " deploy_dist to deploy the contents of dist/ folder to GCS"
@echo " deploy_data to deploy the contents of dist/data/ folder to GCS"
@echo " deploy_userguide to deploy the contents of dist/userguide/ folder to GCS"
@echo " deploy_workbench to deploy the contents of workbench/dist/ folder to GCS"
@echo " clean to remove temporary directories and files (but not dist/)"
@echo " purge to remove temporary directories, cloned repositories and the built environment."
@echo " help to print this help and exit"
Expand Down Expand Up @@ -356,6 +361,18 @@ deploy:
@echo "Application binaries (if they were created) can be downloaded from:"
@echo " * $(DOWNLOAD_DIR_URL)"

deploy_dist:
$(GSUTIL) -m rsync $(DIST_DIR) $(DIST_URL_BASE)

deploy_data:
$(GSUTIL) -m rsync -r $(DIST_DIR)/data $(DIST_URL_BASE)/data

deploy_userguide:
$(GSUTIL) -m rsync -r $(DIST_DIR)/userguide $(DIST_URL_BASE)/userguide

deploy_workbench:
$(GSUTIL) -m rsync -r $(WORKBENCH_DIST_DIR) $(DIST_URL_BASE)/workbench

changelog:
$(RST2HTML5) $(CHANGELOG_SRC) $(CHANGELOG_DEST)

Expand Down
12 changes: 9 additions & 3 deletions codesigning/Makefile
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
ENV := env
CWD := $(shell pwd)
.PHONY: deploy-cloudfunction deploy-worker

deploy-cloudfunction:
Expand All @@ -14,8 +16,12 @@ deploy-cloudfunction:
--source gcp-cloudfunc/

# NOTE: This must be executed from a computer that has SSH access to ncp-inkwell.
deploy-worker:
cd signing-worker && ansible-playbook \
deploy-worker: env
cd signing-worker && $(CWD)/$(ENV)/bin/ansible-playbook \
--ask-become-pass \
--inventory-file inventory.ini \
--inventory inventory.ini \
playbook.yml

env:
python -m venv $(ENV)
$(CWD)/$(ENV)/bin/python -m pip install -r requirements.txt
2 changes: 2 additions & 0 deletions codesigning/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# All runtime requirement for the remote system are handled within the ansible playbook
ansible
14 changes: 11 additions & 3 deletions codesigning/signing-worker/natcap-codesign.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ def download_file(url):
Returns:
``None``
"""
# See the .service file for the WorkingDirectory.
local_filename = url.split('/')[-1]
with requests.get(url, stream=True) as r:
r.raise_for_status()
Expand Down Expand Up @@ -305,6 +306,7 @@ def dmg_has_signature(filename):

def main():
while True:
filename = None
try:
file_to_sign = get_from_queue()
if file_to_sign is None:
Expand Down Expand Up @@ -346,15 +348,21 @@ def main():
filename=filename,
url=file_to_sign['https-url']))
LOGGER.info("Signing complete.")

LOGGER.info(f"Removing {filename}")
os.remove(filename)
except Exception as e:
LOGGER.exception(f"Unexpected error signing file: {e}")
post_to_slack(
SLACK_NOTIFICATION_FAILURE.format(
filename=file_to_sign['https-url'],
traceback=traceback.format_exc()))
finally:
# Always clean up the downloaded file, even if things failed.
# As of debian:trixie, /tmp is a separate filesystem with a hard
# upper limit. Cleaning up files as we go will help avoid running
# out of disk space.
if filename is not None:
LOGGER.info(f"Removing {filename}")
os.remove(filename)

time.sleep(60)


Expand Down
6 changes: 4 additions & 2 deletions codesigning/signing-worker/natcap-codesign.service
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Systemd service for debian:bookworm for signing InVEST windows binaries.
# Systemd service for debian:trixie for signing InVEST windows and mac binaries.
#
# To install this service, copy this onto the host as /etc/systemd/system/natcap-codesign.service
#
Expand Down Expand Up @@ -32,6 +32,8 @@ After=network.target
Type=simple
Restart=always
ExecStart=python3 /opt/natcap-codesign/natcap-codesign.py /opt/natcap-codesign/codesign-cert-chain.pem
User=natcap-codesign
# At least as of debian:trixie, must be run as root to be able to acess the
# installed yubikey.
User=root
Group=natcap-codesign
WorkingDirectory=/tmp
4 changes: 4 additions & 0 deletions codesigning/signing-worker/playbook.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@
- logwatch
- fwlogwatch
- sendmail
- pcscd
- pcsc-tools
- libpcsclite1
- libu2f-udev

- name: Configure logwatch
ansible.builtin.copy:
Expand Down
2 changes: 1 addition & 1 deletion src/natcap/invest/carbon/reporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ def report(file_registry: dict, args_dict: dict, model_spec: ModelSpec,
]

input_raster_stats_table = raster_utils.raster_inputs_summary(
args_dict).to_html(na_rep='')
args_dict, model_spec).to_html(na_rep='')

output_raster_stats_table = raster_utils.raster_workspace_summary(
file_registry).to_html(na_rep='')
Expand Down
Loading
Loading