Skip to content

Copilot/polish 1287 tethys run - #1302

Closed
swainn wants to merge 12 commits into
tethys-run-expressfrom
copilot/polish-1287-tethys-run
Closed

Copilot/polish 1287 tethys run#1302
swainn wants to merge 12 commits into
tethys-run-expressfrom
copilot/polish-1287-tethys-run

Conversation

@swainn

@swainn swainn commented Sep 11, 2026

Copy link
Copy Markdown
Member

This pull request expands the documentation for the project by adding new recipes, updating the recipes gallery, and introducing new images and data resources. The most significant changes are the addition of comprehensive guides for working with REST API endpoints (GET, POST, PUT), a new recipe for adding custom settings, and a guide for integrating Leaflet maps. Several new images and a sample JSON data file are included to support these recipes. Minor improvements and clarifications have also been made to existing documentation.

New and Expanded Documentation Recipes:

  • Added detailed recipes for creating and using REST API endpoints, including guides for GET, POST, and PUT operations, with step-by-step instructions and testing guidance using Postman (docs/recipes/rest_api/add_post_api.rst, docs/recipes/rest_api/add_put_api.rst) [1] [2].
  • Introduced a new recipe for adding custom settings to Tethys apps, showing how to define and utilize custom app settings (docs/recipes/custom_settings.rst).
  • Added a recipe for integrating a Leaflet map into a Tethys app, including instructions for adding required assets, initializing the map, and styling (docs/recipes/maps/add_leaflet_map.rst).

Documentation Structure and Gallery Updates:

  • Updated the recipes gallery and navigation to include new REST API and custom settings recipes, as well as new sections for REST API endpoints in docs/recipes.rst [1] [2] [3] [4].

New Images and Data Resources:

  • Added new images for REST API, POST, PUT, and JavaScript API icons, as well as a custom settings recipe image (docs/images/recipes/icons/rest_api_icon.png, docs/images/recipes/icons/post_endpoint_icon.png, docs/images/recipes/icons/put_endpoint_icon.png, docs/images/recipes/icons/javascript_api_icon.png, docs/images/recipes/custom_settings_recipe.png) [1] [2] [3] [4] [5].
  • Included a sample JSON data file for daily data readings to support API recipes (docs/recipes/resources/daily_data.json).
  • Updated an existing image (docs/images/recipes/component_apps/arcgis.png) with a new version.

Minor Documentation and Configuration Updates:

  • Added the condorpy.static module to the documentation configuration (docs/conf.py).
  • Made minor text improvements and clarifications in existing recipes [1] [2].

These changes significantly enhance the documentation by providing practical, example-driven guides for key Tethys app development features.

Quality Checks

  • At least one new test has been written for new code
  • New code has 100% test coverage
  • Code has been formatted with Black
  • Code has been linted with flake8
  • Docstrings for new methods have been added
  • The documentation has been updated appropriately

jakeymac and others added 11 commits July 21, 2026 10:13
* Scaffold Recipes

* Scaffold Recipes

* Recipes progress - app navigation

* Progress on forms and input recipe

* Recipes for conference

Co-authored-by: Jacob Johnson <jakeymac@users.noreply.github.com>

* Scaffold Recipes

* Recipes progress - app navigation

* Progress on forms and input recipe

* Recipes for conference

Co-authored-by: Jacob Johnson <jakeymac@users.noreply.github.com>

* updated scaffold thumbnail picture file name

* updated scaffold recipte picture

* updated recipe titles

* Added new recipes and files

Co-authored-by: Jacob Johnson <jakeymac@users.noreply.github.com>

* Fixed recipe search bar functionality

* updated recipe file inner labels

* Updated recipes page search bar to include the recipes' titles in searches and updated search bar styling

* Updated styling for recipes search bar

* Addressed comments in the PR

* * Added Map recipes (MapView/MapLayout)

* Added GeoServer recipes

* Add initial custom and service settings recipes documentation

* Add POST API endpoint and REST API documentation

- Created a new POST API endpoint in `add_post_api.rst` for adding data with required parameters: date, value, and count.
- Implemented validation for input parameters and error handling for existing dates.
- Added a new REST API documentation in `add_rest_api.rst` detailing the steps to create a REST API endpoint using Django REST framework.
- Included instructions for testing the API endpoint using Postman.
- Added example data file `daily_data.json` to provide sample data for the API.

* Add REST API recipes, add leaflet map recipe

Co-authored-by: Jacob Johnson <jakeymac@users.noreply.github.com>

* Addressed comments

* * Removed missing link in recipes page
* Updated recipe icon images

* Update docs/recipes/quotas/app_quotas.rst

---------

Co-authored-by: tnicolaysen-aqua <tnicolaysen@aquaveo.com>
Co-authored-by: Jacob Johnson <jakeymac@users.noreply.github.com>
Co-authored-by: Nathan Swain <swainn@users.noreply.github.com>
* Updated documentation to include the TimePicker Gizmo
…led (#1291)

* Add node_modules to STATICFILES_DIRS when STATICFILES_USE_NPM is enabled

- settings.py only appended node_modules to STATICFILES_DIRS when
  STATICFILES_USE_NPM was set under TETHYS_PORTAL_CONFIG
- The generic settings loader later applies any remaining top-level keys
  to the Django settings module, so STATICFILES_USE_NPM declared at the
  top level of the `settings` block would enable NPM mode, switching
  templates from CDN to /static/ URLs without ever adding
  node_modules to the static paths
- The portal then requested local JavaScript and CSS dependencies that
  were never served, returning 404s and leaving the portal unstyled
- Also check for STATICFILES_USE_NPM at the top level so the
  node_modules path is added whenever NPM mode is on, regardless of
  where it's declared

* Format code with python black formatter

---------

Co-authored-by: James-Dolinar <James-Dolinar@users.noreply.github.com>
* Updated login and registration form styles by removing labels to remove empty white space above the fields

* Apply suggestion from @swainn

---------

Co-authored-by: Nathan Swain <swainn@users.noreply.github.com>
* update to latest version of thredds image 5.4 is no longer available

* Let job status be reported to the portal instead of polled for

The jobs table poll views refresh job status on every request. For condor
jobs that opens an SSH connection to the scheduler inside the
request/response cycle, and rendering a workflow's DAG costs one remote
query per node -- measured at ~115 ms per expanded node, so a 26-node DAG
spends around 6 s per poll after the cheaper wins in condorpy, and 26 s
before them.

Two halves, which are only useful together.

Serve from the database:

- CondorWorkflowNode.cached_node_status holds each node's last known condor
  status. apply_node_statuses() persists a mapping of them, and
  update_node_statuses() obtains one with a single query for the whole DAG
  and hands it to the same method.
- CondorWorkflow.node_statuses_updated records when that last happened, and
  node_statuses_are_current reports whether it falls within
  node_statuses_max_age. The poll views render the DAG from the database
  when it does, and read live statuses when it does not.
- CondorWorkflow.cached_statuses reports node status counts from the
  persisted values in the same form as CondorBase.statuses.

Accept reports rather than only asking:

- report_job_status() takes a status, and optionally node statuses, from
  whoever already knows them. update_job_status() asks the job's source what
  the status is, which for condor means the portal SSHes out to rediscover
  what it was just told; this records what it is given.
- Reports are authorised by TethysJob.status_report_token, signed with the
  portal's SECRET_KEY. Nothing has to be configured and no secret has to be
  distributed, and a token authorises reports for one job and no other.
- A terminal status is recorded even when the work that follows it fails, so
  a transient problem syncing results does not leave the portal believing a
  finished job is still running.

The staleness budget is deliberately not update_status_interval. That is the
minimum time between polls of a single job; this is how stale a rendered
status may be. Anything refreshing many jobs takes longer per pass than the
interval for one job, so tying the two together left the statuses never
current precisely when there was enough load for it to matter: a pass over
18 jobs measured 16.8 s against a 10 s interval, and every workflow then
fell back to live reads.

Deciding from the data rather than a setting also means a deployment where
nothing reports behaves exactly as before, with nothing to configure, and
one whose reporter stops falls back to live reads rather than serving
statuses that never advance again.

TethysJob._last_status_update becomes a persisted field. It was previously
set only in memory, so a fresh instance per request always fell back to
execute_time and is_time_to_update() always returned True, meaning the
interval never throttled anything across requests.

Both node-status paths are keyed by the condorpy job name rather than the
raw node name. CondorPyJob.condorpy_job builds that name as
self.name.replace(" ", "_"), so keying on node.name would silently fail to
match for any workflow whose node names contain spaces.

* Carry the job id and a report token on the submitted DAG

The report endpoint is unreachable in practice until something knows which job
it is reporting on and can prove it is allowed to. Attach both to the DAGMan job
as ClassAds at submit time, so a reporter running next to the scheduler can read
them off the queue.

The token is signed with the portal's SECRET_KEY and authorises reports for one
job, so nothing has to be configured and no shared secret is distributed. The ads
are inert where nothing is reporting.

Also fixes test_update_row, which broke when node_statuses_are_current was added
to CondorWorkflow: the test's MagicMock(spec=CondorWorkflow) started returning a
truthy mock for it, so the view read cached_statuses instead of the statuses the
test configured, counted nothing, and reported Submitted instead of Various.

* Shell-quote the report ads when the submit is remote

A ClassAd assignment contains spaces and quotes, and condorpy submits remotely by
joining the arguments into a string for a shell while submitting locally through
argv. Unquoted, the remote shell split the assignment into three words, -append
consumed only the attribute name, and the rest landed after the DAG file:

    ERROR: no arguments allowed after DAG file name(s)

Quote the ad only on the path that reaches a shell; quoting the local path would
make condor read the quotes as part of the value.

* Make the report endpoint dispatchable on the Django version Tethys targets

Every report failed with a 500. On Django 4.2, which Tethys pins, csrf_exempt
wraps an async view in a *sync* function -- the async-aware version arrived in
5.0 -- so Django called the view synchronously, got back an unawaited coroutine
and never a response.

Make the view sync, like update_dask_job_status alongside it, and split get_job
into a sync implementation plus the async wrapper the other views use.

The existing tests could not have caught this: they call the view directly, which
says nothing about whether Django can dispatch to it. Add tests that go through
the URL instead, including one asserting the endpoint is reachable without a CSRF
token, since the reporter has no session to take one from.

Also drop a duplicated handoff route added by mistake.

* Assert the view is callable rather than routable, and run black

The dispatch tests went through the portal's URL, which depends on deployment
settings and resolved to a 404 in CI. The point of them is narrower: the callable
Django invokes must hand back a response rather than an unawaited coroutine.
Assert that directly, and that the view is csrf exempt, with no routing involved.

* Treat a reported status as a fresh status

update_status() only advanced _last_status_update on the branch that fetches the
status from the job's source. A status supplied by a caller -- which is what the
report endpoint does -- set _status and left that clock untouched.

So a job whose status is pushed to us was still refreshed from its source on the
next poll, once per update_status_interval per job, and reporting bought nothing.
Measured against a staging deployment with five concurrent workflows and four
concurrent pollers: ~60 scheduler logins per minute, unchanged whether the
reporter pushed every 30s or every 8s, because the interval never reset.

A report is as current as a fetch, so it should move the same clock.

* Build the DAG from the database when statuses are current

Gating the per-node status read was not enough. The view still reached for
job.condor_object to enumerate the node topology, and that opens a connection to
the scheduler on every poll even when every status is served from the database.

Measured against a staging deployment: each request to update-row and to
update-workflow-nodes-row cost exactly one scheduler login, unthrottled, while
the model layer was correctly quiet -- reading .status on a workflow whose
reported status was under a second old caused none.

When node_statuses_are_current, take the topology from the node rows too. They
carry everything the diagram needs: name, parents and the cached status. Node
counts and parent sets were compared against condor_object on a live 26-node
workflow and are identical, including the node with 24 parents.

cluster_id is the one field the database cannot supply -- condor assigns it at
submit time and it is not persisted -- so it is reported as None on this path.
Nothing in the jobs table reads it.

* Take the status denominator from the database too

Serving the node statuses from the database was not enough on its own: the
percentage calculation still divided by num_jobs, and reading that builds the
condorpy workflow, which connects to the scheduler. Found by instrumenting
condorpy's connect call and reading the stack from a live poll:

    jobs_table.get_job_statuses -> CondorPyWorkflow.num_jobs -> condorpy_workflow

cached_statuses counts node rows, so the matching total is the node count. That
also fixes a mismatch the persisted path had from the start, where node counts
were divided by a job total.

* Document the report endpoint, its token, and the reporting interval

The endpoint had no documentation, and neither did the ClassAd names a reporter
has to read to use it. Both are a contract with software outside this repo, so
name the ads as constants and describe the protocol where the sibling
update-job-status callback is already described.

Records the part that has to be discovered the hard way otherwise: reported node
statuses are only served while fresh, so a reporter must re-send them on an
interval even when nothing has changed. Sending only on change lets them expire
on exactly the steady-state workflows the cache is meant to help.

Also notes that persisting _last_status_update makes update_status_interval
throttle every job type, not just condor, and that the new columns mean
migrations have to be applied before serving the new code.

* Take node statuses only from reports, and trust them less

update_node_statuses was the only caller of condorpy's
node_statuses_by_cluster_id, which no released condorpy has, so it could only
ever log a warning and return nothing. It had no caller in the tree either: the
app-tier updater it was written for was abandoned for a reporter beside the
scheduler, which pushes through the endpoint instead. Removing it drops this
branch's dependency on an unreleased condorpy without touching the measured
result, which comes from the push path.

What is left comes from a remote caller, so treat it that way. A status condor
does not define is ignored rather than persisted, and the read side defaults
instead of indexing, so one that slipped through cannot blank the diagram. A
report matching no node no longer stamps the statuses as current, which had the
views serving a DAG of unexpanded nodes as though it were fresh.

The matched nodes are written in one statement inside a transaction rather than
one save each, since a reporter re-sends them on an interval.

Both DAG sources now yield the same shape and one builder turns it into the
response, so the branches cannot drift and both key on the condorpy job name --
the database source keyed on the raw node name, which differs for any node whose
name contains a space. The percentages come from the counts already fetched
rather than a second query, and cached_statuses is cached_node_statuses, a letter
away from the unrelated job-level cached_status before.

* Guard reported status transitions, and retry results processing that failed

A report could move a job out of a terminal status. That is not a display error:
the next poll re-reaches the terminal status with a non-terminal one recorded, so
update_status treats it as a fresh completion and processes the results a second
time, which for condor re-syncs the remote output. Such a report is refused with
409. A repeated terminal status is still accepted, because a reporter retries
after a timeout, and because Various-Complete is itself terminal and is what a
workflow reaches on its way to Complete. Recognising a finished job goes through
is_terminal, since a custom terminal status is stored as OTH.

The opposite failure was that a terminal status is recorded before the work that
follows it, deliberately, so results that failed to sync could never be fetched:
the job was terminal, nothing was owed, and no retry was possible. A job in a
results status with no completion_time is exactly one that did not get through
that work -- process_results stamps completion_time only once it has returned --
so being told the status again retries it. Rate-limited like a refresh, so
repeated reports for a job whose sync keeps failing cannot each start their own,
and never attempted for a poll, which must not pay for an SCP.

Report tokens now expire. They have to outlive a running job, so the bound is
generous; it is there because the token travels in a job ad that any user who can
query the scheduler can read, and only rotating SECRET_KEY revoked one before.

Asking a job to apply node statuses replaces checking whether it can, so a job
type with parts of its own keeps working, and get_job_sync says that it is
internal.

* Add condory.static to docs mock list.

* Add what's new entries.

---------

Co-authored-by: Nathan Swain <swainn@users.noreply.github.com>
* Update whats_new.rst for upcoming draft release changes

Co-authored-by: swainn <5123221+swainn@users.noreply.github.com>

* Add test coverage for non-Python post install scripts

Co-authored-by: swainn <5123221+swainn@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: swainn <5123221+swainn@users.noreply.github.com>
* Render the captcha field on the login and register pages

PR #1293 replaced whole-form rendering with an explicit list of fields in
order to hide the labels. Enumerating the fields dropped the captcha, which
is not one of them.

With ENABLE_CAPTCHA enabled the field is still required by LoginForm and
RegisterForm, so every submission fails validation on a field the page never
rendered and the form simply re-renders. That locks all users out of a portal
that has captcha turned on.

Render the field when the form has one. get_captcha returns None when
ENABLE_CAPTCHA is off, in which case the form has no such field and
bootstrap_field would raise, so the tag is guarded.

The gap was untested in both directions: the account view tests mock render
and get_template, and the form tests never render a template. Adds tests that
render both pages with captcha enabled and disabled.

* Note the captcha rendering fix in whats_new

* Fix captcha rendering settings in login and register tests
* Fixed spacing issue in map zoom controls

* Fixed vertical spacing issue with multi-line map titles/subtitles in map layout
Copilot AI lite review requested due to automatic review settings September 11, 2026 22:18
…/polish-1287-tethys-run

# Conflicts:
#	docs/whats_new.rst

Co-authored-by: swainn <5123221+swainn@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Critical correctness, security, concurrency, and documentation issues remain unresolved.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

This PR combines job-status and Condor workflow enhancements, a new TimePicker gizmo, portal/layout updates, and expanded documentation recipes and resources.

Changes:

  • Adds signed job-status callbacks, Condor status caching, migrations, and tests.
  • Introduces TimePicker support and related dependency, portal, and styling updates.
  • Expands REST API, Leaflet, custom-settings, and other documentation.
File summaries
File Summary
tethys_portal/urls.py Registers the job-status reporting route.
tethys_portal/templates/tethys_portal/accounts/register.html Updates registration field rendering.
tethys_portal/templates/tethys_portal/accounts/login.html Updates login field rendering.
tethys_portal/settings.py Extends static dependency configuration.
tethys_portal/dependencies.py Adds the bootstrap timepicker dependency.
tethys_layouts/static/tethys_layouts/map_layout/map_layout.css Updates map layout behavior.
tethys_layouts/static/tethys_layouts/map_layout/flat_map.css Adjusts map control styling.
tethys_gizmos/views/gizmos/jobs_table.py Supports cached or live workflow rendering.
tethys_gizmos/templates/tethys_gizmos/gizmos/time_picker.html Adds TimePicker markup.
tethys_gizmos/static/tethys_gizmos/css/time_picker.css Styles the TimePicker widget.
tethys_gizmos/gizmo_options/time_picker.py Defines TimePicker options.
tethys_gizmos/gizmo_options/__init__.py Exports the TimePicker gizmo.
tethys_compute/views/update_status.py Adds the job-status callback endpoint.
tethys_compute/views/__init__.py Exports the status view.
tethys_compute/models/tethys_job.py Adds status tokens and terminal-state handling.
tethys_compute/models/condor/condor_workflow.py Adds Condor workflow status caching.
tethys_compute/models/condor/condor_workflow_node.py Stores cached node statuses.
tethys_compute/migrations/0003_cache_job_statuses.py Adds status cache fields.
tests/unit_tests/test_tethys_portal/test_views/test_accounts_captcha.py Tests CAPTCHA rendering.
tests/unit_tests/test_tethys_gizmos/test_views/test_gizmos/test_jobs_table.py Tests jobs table rendering.
tests/unit_tests/test_tethys_gizmos/test_gizmo_options/test_time_picker.py Tests TimePicker options.
tests/unit_tests/test_tethys_compute/test_views/test_update_status.py Tests status callbacks.
tests/unit_tests/test_tethys_compute/test_models/test_TethysJob.py Tests job status behavior.
tests/unit_tests/test_tethys_compute/test_models/test_CondorWorkflow.py Tests workflow caching and reporting.
tests/unit_tests/test_tethys_cli/test_install_commands.py Tests CLI installation commands.
docs/whats_new/prior_releases.rst Updates prior release documentation.
docs/whats_new.rst Adds release notes.
docs/tethys_sdk/jobs/condor_workflow_type.rst Documents Condor workflow reporting.
docs/tethys_sdk/jobs.rst Documents job status reporting.
docs/tethys_sdk/gizmos/time_picker.rst Documents the TimePicker gizmo.
docs/tethys_sdk/gizmos.rst Updates gizmo documentation.
docs/recipes/web_sockets.rst Updates the WebSockets recipe.
docs/recipes/service_settings.rst Adds service-settings documentation.
docs/recipes/scaffold_an_app_via_command_line.rst Clarifies the scaffold recipe.
docs/recipes/rest_api/use_rest_api_javascript.rst Documents JavaScript REST API usage.
docs/recipes/rest_api/add_rest_api.rst Documents GET API endpoints.
docs/recipes/rest_api/add_put_api.rst Documents PUT API endpoints.
docs/recipes/rest_api/add_post_api.rst Documents POST API endpoints.
docs/recipes/resources/daily_data.json Adds sample API data.
docs/recipes/quotas/user_quotas.rst Updates the user quotas recipe.
docs/recipes/notifications.rst Updates the notifications recipe.
docs/recipes/maps/add_map_layout.rst Updates the map layout recipe.
docs/recipes/maps/add_leaflet_map.rst Adds the Leaflet integration recipe.
docs/recipes/custom_settings.rst Adds the custom-settings recipe.
docs/recipes.rst Updates the recipe gallery and navigation.
docs/images/recipes/icons/rest_api_icon.png Adds REST API gallery artwork.
docs/images/recipes/icons/put_endpoint_icon.png Adds PUT endpoint artwork.
docs/images/recipes/icons/post_endpoint_icon.png Adds POST endpoint artwork.
docs/images/recipes/icons/javascript_api_icon.png Adds JavaScript API artwork.
docs/images/recipes/custom_settings_recipe.png Adds custom-settings artwork.
docs/images/recipes/component_apps/arcgis.png Updates ArcGIS artwork.
docs/conf.py Updates documentation configuration.
Review details

Suppressed comments (19)

docs/recipes.rst:77

  • The new custom_settings.rst page and its custom_settings_recipe.png asset are not referenced by any recipe gallery, so the recipe is not discoverable from the recipes index. Add it to an appropriate recipe-gallery block with the image path and tags.
Rest API
++++++++
.. recipe-gallery:: 
    :layout: multi-row 

    recipes/rest_api/add_rest_api images/recipes/icons/rest_api_icon.png
    recipes/rest_api/add_post_api images/recipes/icons/post_endpoint_icon.png
    recipes/rest_api/add_put_api images/recipes/icons/put_endpoint_icon.png
    recipes/rest_api/use_rest_api_javascript images/recipes/icons/javascript_api_icon.png

docs/recipes.rst:77

  • The new custom_settings and maps/add_leaflet_map recipe pages are not registered in this gallery, while the REST API pages are. They will be orphaned from the recipe navigation despite being part of this documentation addition; add gallery cards for the new pages with appropriate images/tags.
    recipes/rest_api/use_rest_api_javascript images/recipes/icons/javascript_api_icon.png

docs/recipes/custom_settings.rst:9

  • The introduction says this recipe will show how to use custom-setting values inside the app, but the page ends after defining and returning the setting. Add a retrieval/use example (or narrow the introduction) so the recipe matches its stated scope.
This recipe will show you how to create custom settings for your app.  You can create custom settings for your app that can be configured on the app settings page in Tethys Portal.  You can utilize the values of these custom settings inside your app.

docs/recipes/maps/add_leaflet_map.rst:41

  • The recipe creates leaflet_map.js but never adds a script tag for it to the template. Only the Leaflet CDN file is loaded in step 1, so init_map() is never called and the map remains an empty div; add the app-public script reference as in the other JavaScript recipes.
3. Initialize Map with JavaScript
Next, you'll need to add a new JavaScript file in which you will initialize the Leaflet map and add a basemap to it. First, add a file named 'leaflet_map.js' with the following contents:

docs/recipes/maps/add_leaflet_map.rst:64

  • The CSS snippet is described as a file but the recipe never says where to save it or adds a stylesheet link to the template. Merely creating the file will not apply the #leaflet-map height rules, so include the asset-loading step (or provide inline template CSS).
4. Add CSS File
Lastly, you'll need to add a simple CSS file to style everything to make sure your map fills the available space in your app:

docs/recipes/rest_api/add_post_api.rst:31

  • This validation rejects legitimate zero values for value or count because 0 is falsey. Use explicit is None checks, as the PUT recipe already does, so a new record can contain zero readings or counts.
            if not date or not value or not count:

docs/recipes/rest_api/add_post_api.rst:65

  • These two newly added words are misspelled: Selct should be Select, and folloing should be following.
4. Selct **POST** as the method and enter the folloing URL in the URL field:

docs/recipes/rest_api/add_post_api.rst:59

  • Use the possessive-free form its here: it's means it is, which is not intended in this instruction.
1. Right-click on the *My REST API* collection or click on it's "**...**" button and select **Add Request**.

docs/recipes/rest_api/add_post_api.rst:91

  • The value example has an unmatched opening parenthesis, which renders as stray prose and makes the authorization instruction unclear. Remove the trailing (.
    * **Value:** "Token <your token>" (

docs/recipes/rest_api/add_rest_api.rst:139

  • Use the possessive pronoun its rather than the contraction it's in this sentence.
4. Right-click on the new *My REST API* collection or click on it's "**...**" button and select **Add Request**.

docs/recipes/rest_api/add_rest_api.rst:159

  • Correct the misspelling retreive; it should be retrieve.
This is because the endpoint you created requires authentication. To authenticate, you'll need to retreive your account's API token and include that token in the request header.

docs/recipes/rest_api/use_rest_api_javascript.rst:98

  • The endpoint established in add_rest_api.rst uses only TokenAuthentication, but this AJAX request sends no Authorization header or other token. It will therefore receive 401 instead of the daily data; the recipe needs to show a supported way to supply the API key before calling the endpoint.
    function getDailyData(date) {
        return $.ajax({
            url: 'api/daily-data',
            type: 'GET',
            data: {
                date: date
            }
        });

docs/recipes/rest_api/use_rest_api_javascript.rst:155

  • The same callback-closing error is repeated in the final example: both the fail and change callbacks are written as }) without the required callback braces/terminators. The resulting JavaScript is syntactically invalid and should close each call with });.
                })
        })
    });  

docs/recipes/rest_api/use_rest_api_javascript.rst:119

  • Correct the misspelling retreiving; it should be retrieving.
                        console.log("There was an issue retreiving data: ", xhr.statusText);

docs/recipes/rest_api/use_rest_api_javascript.rst:151

  • This new user-facing error message repeats the retreiving misspelling; use retrieving.
                        setDailyDataLabel("There was an issue retreiving data: " + xhr.statusText);

docs/recipes/rest_api/use_rest_api_javascript.rst:101

  • This sentence should say from that request, not form that request.
This function will now return a GET request to the API with a provided date as a parameter. However, now we need to specify what to do with the response form that request. 

docs/recipes/rest_api/use_rest_api_javascript.rst:1

  • The other REST recipes reference use_rest_api_javascript_recipe, but this page defines only use_rest_api_javascript. Sphinx will emit an unresolved-reference warning (or a broken link); rename this target to the referenced label.
.. _use_rest_api_javascript :

docs/recipes/service_settings.rst:9

  • This newly added recipe stops after a one-sentence overview and contains no procedure, code example, or link to the service-settings API. A reader following it cannot configure service settings; add the promised instructions or remove the placeholder page until it is complete.
This recipe will show you how to add service settings to your Tethys app.  Service settings allow you to define configuration options for services that your app uses, such as external APIs or your own services and databases.  Pre-registered services can be assigned to an app in Tethys Portal using the drop down selector.

tethys_portal/urls.py:244

  • The PR description presents this as a documentation/gallery/resources change, but this hunk adds a new state-mutating public endpoint and the branch also changes job-status processing. Please update the scope/acceptance criteria or split the unrelated production behavior so its API, security, and concurrency implications can be reviewed explicitly.
  • Files reviewed: 50/52 changed files
  • Comments generated: 14
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

default='-125,25,-66,49',
)

return (default_map_context_setting)
Comment on lines +81 to +82
)
});
Comment on lines +249 to +251
Note this cannot see a *resubmitted* job whose second run failed to
post-process, because ``execute`` leaves the first run's ``completion_time`` in
place.
Comment on lines +151 to +153
params = request.GET
token = params.get("token")
status = params.get("status")
# custom terminal status is stored as OTH; only a reported status can be trusted
# to be one of the standard codes, since the endpoint rejects anything else above.
reported_code = TethysJob.REVERSE_STATUSES.get(status, status)
if job.is_terminal and reported_code not in TethysJob.TERMINAL_STATUS_CODES:
Comment thread tethys_portal/settings.py
Comment on lines +509 to +512
STATICFILES_USE_NPM = (
TETHYS_PORTAL_CONFIG.pop("STATICFILES_USE_NPM", False)
or "STATICFILES_USE_NPM" in portal_config_settings.keys()
)
Comment thread docs/recipes.rst
recipes/start_tethys images/getting_started/hello_world_login.png [start, app, new_app]
recipes/scaffold_an_app_via_the_portal images/recipes/create_app_form.png [scaffold, app, new_app]
recipes/scaffold_an_app_via_command_line images/recipes/scaffold_pic.png [scaffold, app, new_app]
recipes/start_tethys images/getting_started/hello_world_login.png [start, app, new_app]
Add a Leaflet Map
*****************

** Last Updated:** June 2026
Comment on lines +11 to +13
Now that you've added a GET endpoint to access and retreive your data, let's add some POST functionality.

While a GET endpoint allows you to access or retreive data, a POST endpoint allows you to add to your data.
<p id="daily-data-display">Select a date to view daily data</p>
{% endblock %}

Now we'll begin retreiving data from the API to display in your application dynamically using JavaScript.
Copilot stopped work on behalf of swainn due to an error September 11, 2026 22:41
Copilot stopped work on behalf of swainn due to an error September 11, 2026 22:41
Copilot stopped work on behalf of swainn due to an error September 11, 2026 22:42
Copilot stopped work on behalf of swainn due to an error September 11, 2026 22:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants