Copilot/polish 1287 tethys run - #1302
Conversation
* 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
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
…/polish-1287-tethys-run # Conflicts: # docs/whats_new.rst Co-authored-by: swainn <5123221+swainn@users.noreply.github.com>
There was a problem hiding this comment.
🟡 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.rstpage and itscustom_settings_recipe.pngasset are not referenced by any recipe gallery, so the recipe is not discoverable from the recipes index. Add it to an appropriaterecipe-galleryblock 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_settingsandmaps/add_leaflet_maprecipe 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.jsbut never adds a script tag for it to the template. Only the Leaflet CDN file is loaded in step 1, soinit_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-mapheight 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
valueorcountbecause0is falsey. Use explicitis Nonechecks, 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:
Selctshould beSelect, andfolloingshould befollowing.
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
itshere:it'smeansit 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
itsrather than the contractionit'sin 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 beretrieve.
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.rstuses onlyTokenAuthentication, 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
failandchangecallbacks 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 beretrieving.
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
retreivingmisspelling; useretrieving.
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, notform 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 onlyuse_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) |
| ) | ||
| }); |
| 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. |
| 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: |
| STATICFILES_USE_NPM = ( | ||
| TETHYS_PORTAL_CONFIG.pop("STATICFILES_USE_NPM", False) | ||
| or "STATICFILES_USE_NPM" in portal_config_settings.keys() | ||
| ) |
| 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 |
| 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. |
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:
docs/recipes/rest_api/add_post_api.rst,docs/recipes/rest_api/add_put_api.rst) [1] [2].docs/recipes/custom_settings.rst).docs/recipes/maps/add_leaflet_map.rst).Documentation Structure and Gallery Updates:
docs/recipes.rst[1] [2] [3] [4].New Images and Data Resources:
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].docs/recipes/resources/daily_data.json).docs/images/recipes/component_apps/arcgis.png) with a new version.Minor Documentation and Configuration Updates:
condorpy.staticmodule to the documentation configuration (docs/conf.py).These changes significantly enhance the documentation by providing practical, example-driven guides for key Tethys app development features.
Quality Checks