Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge Aquaveo master which includes V2 PR #22

Open
wants to merge 109 commits into
base: master
Choose a base branch
from
Open

Conversation

msouff
Copy link
Contributor

@msouff msouff commented Mar 20, 2024

Summary by CodeRabbit

  • New Features
    • Introduced Flask application with CORS support and REST API endpoints.
    • Added Flask blueprints for handling routes related to pages and versioned API endpoints.
    • New templates for documentation, license, publications, and resources, including academic contributions.
    • Version 2 of REST endpoints with improved organization and functionality for forecasts and historical data retrieval.
    • Functionality for logging requests to CloudWatch Logs using AWS credentials.
  • Bug Fixes
    • Corrected the project name casing across various templates and README for consistency.
  • Documentation
    • Updated Swagger specification version and corrected project name casing in documentation.
  • Chores
    • Updated .gitignore to exclude specific directories and files.
    • Modified CI/CD pipeline configurations for Helm checks and Docker builds.
    • Changed Dockerfile to use updated environment file name and command.
  • Refactor
    • Restructured controllers for clarity and better organization in version 2 endpoints.
  • Style
    • Updated titles, meta descriptions, and content across HTML templates for consistency and clarity.

@msouff msouff requested a review from rileyhales March 20, 2024 21:15
Copy link

coderabbitai bot commented Mar 20, 2024

Warning

Rate Limit Exceeded

@rileyhales has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 4 minutes and 10 seconds before requesting another review.

How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.
Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.
Please see our FAQ for further information.

Commits Files that changed from the base of the PR and between 23cde35 and 94a018d.

Walkthrough

This update enhances the project by refining configurations, improving the Flask application, and ensuring consistency in documentation. Key changes include the integration of new Flask blueprints for web pages and API endpoints, CI/CD pipeline optimizations, Dockerfile improvements, and standardization of the GEOGLOWS name across all materials. The update also introduces logging capabilities and better organization for managing forecast and historical data, resulting in a more robust and user-friendly application.

Changes

File(s) Summary
.gitignore, .gitlab-ci.yml Added exclusions, streamlined CI/CD with new includes, and updated variables.
Dockerfile Updated conda environment and file references.
README.md, app/.../about.html, base_template.html, documentation.html, home.html, license.html, resources.html Standardized naming and updated content for consistency.
app/app.py, app/blueprint_pages.py, app/v1/..., app/v2/... Enhanced Flask app with new blueprints, API endpoints, and data handling.
app/templates/publications.html Introduced a new template for academic publications.
check_helm_chart, docs/swagger_yml_to_html.py, supervisord.conf Modified scripts and configurations for improved functionality and deployment.

🐰✨
In the world of code, where changes abide,
A rabbit hopped through, with eyes open wide.
"To improve, to enhance," was its cheerful song,
As it tweaked and it tuned, making the project strong.
With each line of code, and each commit pushed,
It left behind magic, in the dusk's gentle hush.
🌟🐾

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

Share

Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>.
    • Generate unit-tests for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit tests for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai generate interesting stats about this repository and render them as a table.
    • @coderabbitai show all the console.log statements in this repository.
    • @coderabbitai read src/utils.ts and generate unit tests.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (invoked as PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger a review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai help to get help.

Additionally, you can add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.

CodeRabbit Configration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • The JSON schema for the configuration file is available here.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/coderabbit-overrides.v2.json

CodeRabbit Discord Community

Join our Discord Community to get help, request features, and share feedback.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Review Status

Actionable comments generated: 10

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between 7452361 and 00546b1.
Files ignored due to path filters (45)
  • app/geometry/1_simplified_region_boundary_geojsons.zip is excluded by: !**/*.zip
  • app/geometry/africa-geoglows-comid_lat_lon_z.pickle is excluded by: !**/*.pickle
  • app/geometry/australia-geoglows-comid_lat_lon_z.pickle is excluded by: !**/*.pickle
  • app/geometry/boundaries.pickle is excluded by: !**/*.pickle
  • app/geometry/central_america-geoglows-comid_lat_lon_z.pickle is excluded by: !**/*.pickle
  • app/geometry/central_asia-geoglows-comid_lat_lon_z.pickle is excluded by: !**/*.pickle
  • app/geometry/east_asia-geoglows-comid_lat_lon_z.pickle is excluded by: !**/*.pickle
  • app/geometry/europe-geoglows-comid_lat_lon_z.pickle is excluded by: !**/*.pickle
  • app/geometry/islands-geoglows-comid_lat_lon_z.pickle is excluded by: !**/*.pickle
  • app/geometry/japan-geoglows-comid_lat_lon_z.pickle is excluded by: !**/*.pickle
  • app/geometry/middle_east-geoglows-comid_lat_lon_z.pickle is excluded by: !**/*.pickle
  • app/geometry/north_america-geoglows-comid_lat_lon_z.pickle is excluded by: !**/*.pickle
  • app/geometry/region_coordinate_files/africa-geoglows/comid_lat_lon_z.csv is excluded by: !**/*.csv
  • app/geometry/region_coordinate_files/australia-geoglows/comid_lat_lon_z.csv is excluded by: !**/*.csv
  • app/geometry/region_coordinate_files/bounding_boxes.csv is excluded by: !**/*.csv
  • app/geometry/region_coordinate_files/central_america-geoglows/comid_lat_lon_z.csv is excluded by: !**/*.csv
  • app/geometry/region_coordinate_files/central_asia-geoglows/comid_lat_lon_z.csv is excluded by: !**/*.csv
  • app/geometry/region_coordinate_files/east_asia-geoglows/comid_lat_lon_z.csv is excluded by: !**/*.csv
  • app/geometry/region_coordinate_files/europe-geoglows/comid_lat_lon_z.csv is excluded by: !**/*.csv
  • app/geometry/region_coordinate_files/global_coordinate_file.csv is excluded by: !**/*.csv
  • app/geometry/region_coordinate_files/islands-geoglows/comid_lat_lon_z.csv is excluded by: !**/*.csv
  • app/geometry/region_coordinate_files/japan-geoglows/comid_lat_lon_z.csv is excluded by: !**/*.csv
  • app/geometry/region_coordinate_files/middle_east-geoglows/comid_lat_lon_z.csv is excluded by: !**/*.csv
  • app/geometry/region_coordinate_files/north_america-geoglows/comid_lat_lon_z.csv is excluded by: !**/*.csv
  • app/geometry/region_coordinate_files/south_america-geoglows/comid_lat_lon_z.csv is excluded by: !**/*.csv
  • app/geometry/region_coordinate_files/south_asia-geoglows/comid_lat_lon_z.csv is excluded by: !**/*.csv
  • app/geometry/region_coordinate_files/west_asia-geoglows/comid_lat_lon_z.csv is excluded by: !**/*.csv
  • app/geometry/south_america-geoglows-comid_lat_lon_z.pickle is excluded by: !**/*.pickle
  • app/geometry/south_asia-geoglows-comid_lat_lon_z.pickle is excluded by: !**/*.pickle
  • app/geometry/west_asia-geoglows-comid_lat_lon_z.pickle is excluded by: !**/*.pickle
  • app/static/images/about1.jpg is excluded by: !**/*.jpg
  • app/static/images/about2.jpg is excluded by: !**/*.jpg
  • app/static/images/about3.jpg is excluded by: !**/*.jpg
  • app/static/images/about4.jpg is excluded by: !**/*.jpg
  • app/static/images/aquaveologo.png is excluded by: !**/*.png
  • app/static/images/byulogo.png is excluded by: !**/*.png
  • app/static/images/ecmwflogo.png is excluded by: !**/*.png
  • app/static/images/favicon.ico is excluded by: !**/*.ico
  • app/static/images/geoglowslogo.png is excluded by: !**/*.png
  • assets/streamflow_animation.png is excluded by: !**/*.png
  • demos/input_example.json is excluded by: !**/*.json
  • docker-compose.yml is excluded by: !**/*.yml
  • docs/swagger_doc.yaml is excluded by: !**/*.yaml
  • environment.yaml is excluded by: !**/*.yaml
  • helm/gsprestapi/Chart.yaml is excluded by: !**/*.yaml
Files selected for processing (33)
  • .gitignore (1 hunks)
  • .gitlab-ci.yml (5 hunks)
  • Dockerfile (1 hunks)
  • README.md (1 hunks)
  • app/app.py (1 hunks)
  • app/blueprint_pages.py (1 hunks)
  • app/templates/about.html (3 hunks)
  • app/templates/base_template.html (5 hunks)
  • app/templates/documentation.html (2 hunks)
  • app/templates/home.html (1 hunks)
  • app/templates/license.html (3 hunks)
  • app/templates/publications.html (1 hunks)
  • app/templates/resources.html (2 hunks)
  • app/v1/init.py (1 hunks)
  • app/v1/analytics.py (1 hunks)
  • app/v1/blueprint.py (1 hunks)
  • app/v1/v1_controllers_forecasts.py (1 hunks)
  • app/v1/v1_controllers_historical.py (1 hunks)
  • app/v1/v1_functions.py (1 hunks)
  • app/v1/v1_utilities.py (1 hunks)
  • app/v2/init.py (1 hunks)
  • app/v2/analytics.py (1 hunks)
  • app/v2/blueprint.py (1 hunks)
  • app/v2/constants.py (1 hunks)
  • app/v2/controllers_forecasts.py (5 hunks)
  • app/v2/controllers_historical.py (1 hunks)
  • app/v2/controllers_misc.py (1 hunks)
  • app/v2/data.py (1 hunks)
  • app/v2/response_formatters.py (1 hunks)
  • check_helm_chart (1 hunks)
  • docs/index.html (2 hunks)
  • docs/swagger_yml_to_html.py (1 hunks)
  • supervisord.conf (1 hunks)
Files not summarized due to errors (1)
  • docs/index.html: Error: Message exceeds token limit
Files skipped from review due to trivial changes (8)
  • app/templates/about.html
  • app/templates/base_template.html
  • app/templates/home.html
  • app/templates/resources.html
  • app/v1/init.py
  • app/v1/v1_functions.py
  • app/v1/v1_utilities.py
  • app/v2/init.py
Additional comments: 49
app/v2/constants.py (2)
  • 3-3: The conversion factor from cubic meters to cubic feet is accurate.
  • 4-4: Ensure NUM_DECIMALS is applied consistently across the application for numerical precision.
Verification successful

The usage of NUM_DECIMALS in app/v2/controllers_forecasts.py for rounding operations confirms that it is applied consistently across the application for numerical precision. This aligns with the intended use of ensuring numerical precision is maintained uniformly.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Search for usage of NUM_DECIMALS across Python files to ensure consistent application.
rg "NUM_DECIMALS" --type py

Length of output: 736

supervisord.conf (1)
  • 5-6: The updated uwsgi configuration points to a specific virtual environment, which is a common practice. Ensure the new configuration is tested and works as expected in the deployment environment.
app/app.py (1)
  • 12-30: The Flask app configuration and blueprint registrations follow standard practices. Consider adding comments to explain the purpose of each major section (e.g., CORS setup, blueprint registrations) for better clarity and maintainability.
Dockerfile (1)
  • 8-13: The updates to the conda environment creation command and the reference to environment.yaml are correct. Ensure the environment setup is tested and works as expected in the Docker container.
app/blueprint_pages.py (1)
  • 1-53: The Flask blueprint setup, routes, and error handling in blueprint_pages.py follow standard practices. Consider adding comments to explain the purpose of each route and the custom 404 error handler for better clarity and maintainability.
check_helm_chart (1)
  • 27-32: Consider enhancing the error handling by providing more detailed messages based on the HTTP status code returned by the server. This can help in diagnosing issues with chart uploads more effectively.
elif [ ${RESULT} -ne 200 ]; then
-    echo "Unknown error while trying to retrieve chart from remote" >&2
+    case ${RESULT} in
+    401)
+        echo "Unauthorized: Check if the ECCR_USER and ECCR_PASSWORD environment variables are correctly set." >&2
+        ;;
+    403)
+        echo "Forbidden: The user does not have permission to access the chart repository." >&2
+        ;;
+    500)
+        echo "Internal Server Error: There might be a problem with the chart repository server." >&2
+        ;;
+    *)
+        echo "An unexpected error occurred while trying to retrieve the chart from remote. HTTP status code: ${RESULT}" >&2
+        ;;
+    esac
     exit -2
fi
app/v2/response_formatters.py (3)
  • 10-14: The implementation of df_to_csv_flask_response is correct and follows best practices for generating CSV responses in Flask.
  • 17-25: The df_to_jsonify_response function correctly formats DataFrame data into a JSON response. Consider explicitly documenting the choice to replace NaN values with empty strings, as this might impact consumers of the API depending on the context.
  • 28-42: The new_json_template function is well-implemented, providing a clear and reusable structure for JSON responses.
app/v1/analytics.py (1)
  • 23-28: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [1-44]

The log_request function correctly logs requests to AWS CloudWatch. Consider adding error handling for the AWS CloudWatch logging operation to gracefully handle potential issues with logging, such as network errors or misconfigurations.

.gitignore (1)
  • 185-190: The updates to .gitignore are appropriate and follow best practices for excluding editor configurations and sensitive configuration files from the repository.
docs/swagger_yml_to_html.py (1)
  • 21-21: The update to the title block in the HTML template aligns with the naming consistency objective of the PR.
app/v2/data.py (2)
  • 47-54: The get_return_periods_dataframe function is well-implemented, correctly retrieving and processing return period data from an S3 bucket.
  • 57-61: The find_available_dates function correctly identifies available forecast dates using file listing and sorting. Good use of glob and natsort.
.gitlab-ci.yml (3)
  • 5-11: The inclusion of helm-check.yml and kaniko-build.yml enhances the CI/CD pipeline by integrating additional checks and builds. This aligns with best practices for CI/CD pipeline configuration.
  • 17-34: The updates to variables for the Helm stages are appropriate and improve the clarity and maintainability of the CI/CD pipeline configuration.
  • 52-57: The before_script configuration for Docker authentication in the Build Tag stage is a good security practice. Verify that the Docker authentication works as expected in the CI/CD environment.
app/templates/license.html (2)
  • 2-2: Ensure the updated title aligns with the project's naming conventions and accurately reflects the content of the page.
  • 25-63: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [28-80]

The update to the data license and the addition of a disclaimer are significant improvements. It's important to verify that the Creative Commons license chosen (CC BY 4.0) is appropriate for the data being shared and that the disclaimer is clear and understandable to users.

app/v2/controllers_historical.py (5)
  • 16-27: The function _get_retrospective_df efficiently retrieves retrospective data from an S3 bucket using xarray and pandas. Ensure that error handling is in place for potential issues with data access or transformation.
  • 51-63: The daily_averages function demonstrates good use of pandas for data manipulation. Ensure consistency in date formatting across all functions for clarity and usability.
  • 66-77: In the monthly_averages function, consider adding comments to explain the logic, especially for the grouping and averaging process, to improve maintainability.
  • 80-91: The yearly_averages function is concise and follows similar patterns as other functions. Verify that the date range handling meets the application's requirements.
  • 94-110: The return_periods function introduces a new response format. Ensure that the response structure is documented and consistent with other API endpoints.
app/templates/publications.html (2)
  • 2-2: The updated title accurately reflects the content of the page and aligns with the project's naming conventions.
  • 13-83: The addition of publications is a valuable update for users interested in the research behind the GEOGLOWS ECMWF Streamflow Model. Ensure that all publication links are valid and accessible.
app/v1/v1_controllers_historical.py (3)
  • 9-9: The use of jsonify and make_response from Flask is appropriate for generating responses. Ensure that all responses are consistent in format across the API.
  • 11-12: The relative imports for constants and functions are correctly updated to maintain modularity and ease of maintenance.
  • 6-15: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [1-228]

Overall, the changes in app/v1/v1_controllers_historical.py improve the organization and readability of the code. Ensure that error handling and input validation are consistently applied across all functions to enhance robustness and security.

+ # Add input validation and error handling where necessary
app/v2/blueprint.py (3)
  • 25-25: The creation of the Flask blueprint for version 2 endpoints is correctly implemented. Ensure that the blueprint name is unique and descriptive.
  • 83-173: The handle_request function effectively parses and validates request parameters. Consider adding more detailed error messages to help users correct their requests.
  • 176-185: Error handling for ValueError and general exceptions is a good practice. Ensure that logging is appropriately configured to capture sufficient details for debugging.
app/v2/controllers_forecasts.py (7)
  • 10-17: The updated import statements and the use of relative imports improve modularity and maintainability. Ensure that all necessary modules are imported and unused imports are removed.
  • 23-39: The hydroviewer function integrates multiple data sources to provide a comprehensive response. Ensure that the data integration logic is accurate and that the response format meets the API's standards.
  • 58-140: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [51-82]

The forecast function demonstrates efficient data manipulation using numpy and pandas. Consider adding error handling for potential issues with data access or transformation.

  • 87-118: The forecast_stats function correctly calculates various statistical measures. Verify that the percentile calculations are appropriate for the application's requirements.
  • 58-140: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [123-166]

In the forecast_ensembles function, the handling of ensemble filtering is well-implemented. Ensure that the ensemble parameter is validated to prevent errors.

+ # Validate ensemble parameter
  • 188-228: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [172-206]

The forecast_records function retrieves historical forecast records. Ensure that the date range validation is robust and that error messages are clear for users.

  • 212-227: The forecast_dates function provides a list of available forecast dates. Consider adding caching to improve performance if the available dates do not change frequently.
app/v1/v1_controllers_forecasts.py (6)
  • 9-10: The update to relative imports for constants and v1_functions is a good practice for module organization and helps avoid import errors in a package structure.
  • 5-13: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [12-93]

The forecast_stats function integrates well with the changes to relative imports and adheres to best practices in handling parameters, units, and file paths.

  • 5-13: > 📝 NOTE

This review was outside the diff hunks, and no overlapping diff hunk was found. Original lines [95-169]

The forecast_ensembles function correctly uses the updated relative imports and follows best practices in its implementation.

  • 5-13: > 📝 NOTE

This review was outside the diff hunks, and no overlapping diff hunk was found. Original lines [171-234]

The forecast_warnings function correctly uses the updated relative imports and adheres to best practices in handling different scenarios for regions and forecast dates.

  • 5-13: > 📝 NOTE

This review was outside the diff hunks, and no overlapping diff hunk was found. Original lines [236-287]

The forecast_records function integrates well with the changes to relative imports and adheres to best practices in handling parameters, units, and file paths.

  • 5-13: > 📝 NOTE

This review was outside the diff hunks, and no overlapping diff hunk was found. Original lines [289-307]

The available_dates function correctly uses the updated relative imports and follows best practices in its implementation.

app/templates/documentation.html (2)
  • 2-2: The update to the title casing to "GEOGLOWS" aligns with the PR's objective to standardize the project name's casing.
  • 11-11: The update of the version number in the Swagger specification to "1.5.0" likely reflects the introduction of new features or significant updates in the API.
docs/index.html (1)
  • 11-11: The JavaScript code for removing the Swagger UI top bar is effective but consider the maintainability of this approach. Future updates to Swagger UI might change class names or the structure, requiring updates to this script. Documenting this customization or exploring alternative, more maintainable approaches could be beneficial.

docs/index.html Outdated
@@ -8,7 +8,7 @@
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js"></script>
<script>
window.onload = function() {
const spec = {"swagger": "2.0", "info": {"title": "GEOGloWS ECMWF Streamflow Data Service", "description": "A REST Data Service to access high resolution streamflow forecasts from around the world", "version": "2.0.0"}, "host": "geoglows.ecmwf.int", "basePath": "/api", "schemes": ["https", "http"], "paths": {"/Hydroviewer/{reach_id}/{return_format}": {"get": {"description": "A shorthand for retrieving the forecast records and stats, and return periods, usually all plotted together.", "summary": "Returns forecast records, forecast stats, and return periods.", "parameters": [{"name": "reach_id", "in": "path", "description": "The stream reach's unique ID also refered to as common identifier (COMID). If the ID is not known, use the getReachID method.", "type": "number", "format": "integer", "required": true}, {"name": "return_format", "in": "path", "required": true, "description": "The format of the response", "type": "string", "default": "csv", "enum": ["csv", "json"]}, {"name": "date", "in": "query", "description": "The given date for the forecast of interest given as YYYYMMDD (e.g. 20201020). If left blank it defaults to the most recent date. This API provides access to data within the last 30 days.", "type": "string", "pattern": "^[0-9]{4}(0[1-9]|1[0-2])(0[1-9]|[1-2][0-9]|3[0-1])(.(00|12)|)$"}], "produces": ["text/csv", "application/json"], "responses": {"200": {"description": "The response body will contain a time series along with metadata about the stream reach of interest."}, "400": {"description": "Bad request. Check request and paramaters.", "examples": {"error": "An unexpected error occured."}}, "422": {"description": "Missing a required parameter.", "examples": {"error": "region is required as input."}}}}}, "/Forecast/{reach_id}/{return_format}": {"get": {"description": "This operation returns the average of the ensemble forecast.", "summary": "Returns average forecasted flow", "parameters": [{"name": "reach_id", "in": "path", "description": "The stream reach's unique ID also refered to as common identifier (COMID). If the ID is not known, use the getReachID method.", "type": "number", "format": "integer", "required": true}, {"name": "return_format", "in": "path", "required": true, "description": "The format of the response", "type": "string", "default": "csv", "enum": ["csv", "json"]}, {"name": "date", "in": "query", "description": "The given date for the forecast of interest given as YYYYMMDD (e.g. 20201020). If left blank it defaults to the most recent date. This API provides access to data within the last 30 days.", "type": "string", "pattern": "^[0-9]{4}(0[1-9]|1[0-2])(0[1-9]|[1-2][0-9]|3[0-1])(.(00|12)|)$"}], "produces": ["text/csv", "application/json"], "responses": {"200": {"description": "The response body will contain a time series along with metadata about the stream reach of interest."}, "400": {"description": "Bad request. Check request and paramaters.", "examples": {"error": "An unexpected error occured."}}, "422": {"description": "Missing a required parameter.", "examples": {"error": "region is required as input."}}}}}, "/ForecastStats/{reach_id}/{return_format}": {"get": {"description": "This operation returns statistics calculated from 51 forecast ensemble members. A successful response will return a time series with date-value pairs.", "summary": "Return basic forecast statistics", "parameters": [{"name": "reach_id", "in": "path", "description": "The stream reach's unique ID also refered to as common identifier (COMID). If the ID is not known, use the getReachID method.", "type": "number", "format": "integer", "required": true}, {"name": "return_format", "in": "path", "required": true, "description": "The format of the response", "type": "string", "default": "csv", "enum": ["csv", "json"]}, {"name": "date", "in": "query", "description": "The given date for the forecast of interest given as YYYYMMDD (e.g. 20201020). If left blank it defaults to the most recent date. This API provides access to data within the last 30 days.", "type": "string", "pattern": "^[0-9]{4}(0[1-9]|1[0-2])(0[1-9]|[1-2][0-9]|3[0-1])(.(00|12)|)$"}], "produces": ["text/csv", "application/json"], "responses": {"200": {"description": "The response body will contain a time series along with metadata about the stream reach of interest."}, "400": {"description": "Bad request. Check request and paramaters.", "examples": {"error": "An unexpected error occured."}}, "422": {"description": "Missing a required parameter.", "examples": {"error": "region is required as input."}}}}}, "/ForecastEnsembles/{reach_id}/{return_format}": {"get": {"description": "This operation returns a timeseries for each of the 51 normal forecast ensemble members and the 52nd higher resolution forecast. A successful response will return a time series with date-value pairs.", "summary": "Return forecast ensembles", "parameters": [{"name": "reach_id", "in": "path", "description": "The stream reach's unique ID also refered to as common identifier (COMID). If the ID is not known, use the getReachID method.", "type": "number", "format": "integer", "required": true}, {"name": "date", "in": "query", "description": "The given date for the forecast of interest given as YYYYMMDD (e.g. 20201020). If left blank it defaults to the most recent date. This API provides access to data within the last 30 days.", "type": "string", "pattern": "^[0-9]{4}(0[1-9]|1[0-2])(0[1-9]|[1-2][0-9]|3[0-1])(.(00|12)|)$"}, {"name": "ensemble", "in": "query", "description": "The desired forecast ensemble(s). Input can be:\n - An individual ensemble (i.e. 5)\n - A group of ensembles separated by commas (i.e. 1,5,9)\n - A range of ensembles separated by a dash (i.e. 3-15)\n - A combination of individual numbers and ranges (i.e. 1,5,7-16,24-35)", "type": "string", "default": "all"}, {"name": "return_format", "in": "path", "required": true, "description": "The format of the response", "type": "string", "default": "csv", "enum": ["csv", "json"]}], "produces": ["text/csv", "application/json"], "responses": {"200": {"description": "The response body will contain a time series for each ensemble along with metadata about the stream reach of interest."}, "400": {"description": "Bad request. Check request and paramaters.", "examples": {"error": "An unexpected error occured."}}, "422": {"description": "Missing a required parameter.", "examples": {"error": "region is required as input."}}}}}, "/ForecastRecords/{reach_id}/{return_format}": {"get": {"description": "This retrieves the rolling record of the mean of the forecasted streamflow during the first 24 hours of each day's forecast. That is, each day day after the\nstreamflow forecasts are computed, the average of first 8 of the 3-hour timesteps are recorded to a csv. This retrives that rolling record", "summary": "Return rolling record of average flows", "parameters": [{"name": "reach_id", "in": "path", "description": "The stream reach's unique ID also refered to as common identifier (COMID). If the ID is not known, use the getReachID method.", "type": "number", "format": "integer", "required": true}, {"name": "start_date", "in": "query", "description": "A date in YYYYMMDD format when you would like to start retrieving data (if available). Defaults to Jan 1 of the current year.", "type": "string", "pattern": "^[0-9]{4}(0[1-9]|1[0-2])(0[1-9]|[1-2][0-9]|3[0-1])$"}, {"name": "end_date", "in": "query", "description": "A date in YYYYMMDD format when you would like to stop retrieving data (if available). Defaults to Dec 31 of the current year.", "type": "string", "pattern": "^[0-9]{4}(0[1-9]|1[0-2])(0[1-9]|[1-2][0-9]|3[0-1])$"}], "produces": ["text/csv", "application/json"], "responses": {"200": {"description": "The response body will contain a time series for the specified stream reach"}, "400": {"description": "Bad request. Check request and paramaters.", "examples": {"error": "An unexpected error occured."}}, "422": {"description": "Missing a required parameter.", "examples": {"error": "region is required as input."}}}}}, "/ForecastAnomalies/{reach_id}/{return_format}": {"get": {"description": "Computes the deviations from the daily averages", "summary": "Calculates the differences between the daily average and the forecasted flow for each day", "parameters": [{"name": "reach_id", "in": "path", "description": "The stream reach's unique ID also refered to as common identifier (COMID). If the ID is not known, use the getReachID method.", "type": "number", "format": "integer", "required": true}, {"name": "start_date", "in": "query", "description": "A date in YYYYMMDD format when you would like to start retrieving data (if available). Defaults to Jan 1 of the current year.", "type": "string", "pattern": "^[0-9]{4}(0[1-9]|1[0-2])(0[1-9]|[1-2][0-9]|3[0-1])$"}, {"name": "end_date", "in": "query", "description": "A date in YYYYMMDD format when you would like to stop retrieving data (if available). Defaults to Dec 31 of the current year.", "type": "string", "pattern": "^[0-9]{4}(0[1-9]|1[0-2])(0[1-9]|[1-2][0-9]|3[0-1])$"}], "produces": ["text/csv", "application/json"], "responses": {"200": {"description": "The response body will contain a time series for the specified stream reach"}, "400": {"description": "Bad request. Check request and paramaters.", "examples": {"error": "An unexpected error occured."}}, "422": {"description": "Missing a required parameter.", "examples": {"error": "region is required as input."}}}}}, "/ForecastWarnings/{return_format}": {"get": {"description": "This method returns a csv created to summarize the potential return period level flow events coming to the reaches in a specified region. The CSV contains a column for the reach_id, lat of the reach, lon of the reach, maximum forecasted flow in the next 15 day forecast, and a column for each of the return periods (2, 10, 20, 25, 50, 100) which will contain the date when the forecast is first expected to pass that threshold.", "summary": "Returns potential warnings for the forecast", "parameters": [{"name": "region", "in": "path", "description": "the name of one of the regions that the streamflow forecasts are generated for. You can find this with the AvailableRegions method.", "type": "string", "default": "all", "required": true}, {"name": "forecast_date", "in": "query", "description": "If multiple forecast dates are available, provide the date in the format YYYYMMDD.HH of the forecast you would like to query. Use the AvailableDates method if you need to find out which dates are available. Defaults to the most recent available data.", "type": "string", "format": "string"}, {"name": "return_format", "in": "path", "required": true, "description": "The format of the response", "type": "string", "default": "csv", "enum": ["csv", "json"]}], "produces": ["text/csv", "application/json"], "responses": {"200": {"description": "The response body will contain a csv table with the information previous mentioned."}, "400": {"description": "Bad request. Check request and paramaters.", "examples": {"error": "An unexpected error occured."}}, "422": {"description": "Missing a required parameter.", "examples": {"error": "region or both lat and lon are required as input"}}}}}, "/ForecastDates": {"get": {"description": "This operation returns the available forecast dates in JSON format.", "summary": "Available dates", "produces": ["application/json"], "responses": {"200": {"description": "The response body will contain a list of available dates."}, "204": {"description": "Succesful request but no regions found.", "examples": {"message": "No dates available."}}, "400": {"description": "Bad request. Check request and paramaters.", "examples": {"error": "An unexpected error occured."}}, "422": {"description": "Missing a required parameter.", "examples": {"error": "region is required as input."}}}}}, "/Hindcast/{reach_id}/{return_format}": {"get": {"description": "This operation returns simulated streamflow data based on the ERA-5 dataset. A successful response will return a time series with date-value pairs.", "summary": "Return historic simulation", "parameters": [{"name": "reach_id", "in": "path", "description": "The stream reach's unique ID also refered to as common identifier (COMID). If the ID is not known, use the getReachID method.", "type": "number", "format": "integer", "required": true}, {"name": "return_format", "in": "path", "required": true, "description": "The format of the response", "type": "string", "default": "csv", "enum": ["csv", "json"]}], "produces": ["text/csv", "application/json"], "responses": {"200": {"description": "The response body will contain a time series along with metadata about the stream reach of interest."}, "400": {"description": "Bad request. Check request and paramaters.", "examples": {"error": "An unexpected error occured."}}, "422": {"description": "Missing a required parameter.", "examples": {"error": "region is required as input."}}}}}, "/DailyAverages/{reach_id}/{return_format}": {"get": {"description": "This operation returns the average flow for each day of the year for the Historic Simulation", "summary": "Return historic simulation's daily averages", "parameters": [{"name": "reach_id", "in": "path", "description": "The stream reach's unique ID also refered to as common identifier (COMID). If the ID is not known, use the getReachID method.", "type": "number", "format": "integer", "required": true}, {"name": "return_format", "in": "path", "required": true, "description": "The format of the response", "type": "string", "default": "csv", "enum": ["csv", "json"]}], "produces": ["text/csv", "application/json"], "responses": {"200": {"description": "The response body will contain a time series along with metadata about the stream reach of interest."}, "400": {"description": "Bad request. Check request and paramaters.", "examples": {"error": "An unexpected error occured."}}, "422": {"description": "Missing a required parameter.", "examples": {"error": "region is required as input."}}}}}, "/MonthlyAverages/{reach_id}/{return_format}": {"get": {"description": "This operation returns the average flow for each month of the year for the Historic Simulation", "summary": "Return historic simulation's monthly averages", "parameters": [{"name": "reach_id", "in": "path", "description": "The stream reach's unique ID also refered to as common identifier (COMID). If the ID is not known, use the getReachID method.", "type": "number", "format": "integer", "required": true}, {"name": "return_format", "in": "path", "required": true, "description": "The format of the response", "type": "string", "default": "csv", "enum": ["csv", "json"]}], "produces": ["text/csv", "application/json"], "responses": {"200": {"description": "The response body will contain a time series along with metadata about the stream reach of interest."}, "400": {"description": "Bad request. Check request and paramaters.", "examples": {"error": "An unexpected error occured."}}, "422": {"description": "Missing a required parameter.", "examples": {"error": "region is required as input."}}}}}, "/ReturnPeriods/{reach_id}/{return_format}": {"get": {"description": "This operation returns the 2, 5, 10, 25, 50, and 100 year return period based on the 40-years simulated streamflow data and using the Gumbel Method. A successful response will return key-value pairs for earch return period along with metadata.", "summary": "Return historic simulation", "parameters": [{"name": "reach_id", "in": "path", "description": "The stream reach's unique ID also refered to as common identifier (COMID). If the ID is not known, use the getReachID method.", "type": "number", "format": "integer", "required": true}, {"name": "return_format", "in": "path", "required": true, "description": "The format of the response", "type": "string", "default": "csv", "enum": ["csv", "json"]}], "produces": ["text/csv", "application/json"], "responses": {"200": {"description": "The response body will contain a key-value pairs for each return period along with metadata about the stream reach of interest."}, "400": {"description": "Bad request. Check request and paramaters.", "examples": {"error": "An unexpected error occured."}}, "422": {"description": "Missing a required parameter.", "examples": {"error": "region is required as input."}}}}}, "/getReachID": {"get": {"description": "Find the Reach ID nearest a point using latitude and longitude coordinates", "summary": "Find the Reach ID nearest a point using latitude and longitude coordinates", "parameters": [{"name": "lat", "in": "query", "required": true, "description": "The latitude of a point to search", "type": "number", "format": "float"}, {"name": "lon", "in": "query", "required": true, "description": "The longitude of a point to search", "type": "number", "format": "float"}], "produces": ["application/json"], "responses": {"200": {"description": null}, "400": {"description": "Bad request. Check request and paramaters.", "examples": {"error": "An unexpected error occured."}}, "422": {"description": "Missing a required parameter.", "examples": {"error": "region is required as input."}}}}}, "/ForecastStats": {"get": {"description": "This operation returns statistics calculated from 51 forecast ensemble members. A successful response will return a time series with date-value pairs.", "summary": "Return basic forecast statistics (if unable to provide reach_id, lat and lon can be provided as query arguments)", "parameters": [{"name": "lat", "in": "query", "description": "A given latitude used to find the closest stream reach. Must be used together with `lon`. The spatial query uses the center point of the stream reach to determine proximity. This parameter can be given instead of providing `reach_id` in the path. A lat/lon spatial query will take longer than providing the reach_id. It is recommended that the reach_id be used instead once it is known.", "type": "number", "format": "float"}, {"name": "lon", "in": "query", "description": "A given longitude used to find the closest stream reach. Must be used together with `lat`. The spatial query uses the center point of the stream reach to determine proximity. This parameter can be given instead of providing `reach_id` in the path. A lat/lon spatial query will take longer than providing the reach_id. It is recommended that the reach_id be used instead once it is known.", "type": "number", "format": "float"}, {"name": "date", "in": "query", "description": "The given date for the forecast of interest given as YYYYMMDD (e.g. 20201020). If left blank it defaults to the most recent date. This API provides access to data within the last 30 days.", "type": "string", "pattern": "^[0-9]{4}(0[1-9]|1[0-2])(0[1-9]|[1-2][0-9]|3[0-1])(.(00|12)|)$"}, {"name": "return_format", "in": "query", "description": "The format of the time series response.", "type": "string", "default": "csv", "enum": ["csv", "json"]}], "produces": ["text/csv", "application/json"], "responses": {"200": {"description": "The response body will contain a time series along with metadata about the stream reach of interest."}, "400": {"description": "Bad request. Check request and paramaters.", "examples": {"error": "An unexpected error occured."}}, "422": {"description": "Missing a required parameter.", "examples": {"error": "region is required as input."}}}}}, "/ForecastEnsembles": {"get": {"description": "This operation returns a timeseries for each of the 51 normal forecast ensemble members and the 52nd higher resolution forecast. A successful response will return a time series with date-value pairs.", "summary": "Return forecast ensembles", "parameters": [{"name": "lat", "in": "query", "description": "A given latitude used to find the closest stream reach. Must be used together with `lon`. The spatial query uses the center point of the stream reach to determine proximity. This parameter can be given instead of providing `reach_id` in the path. A lat/lon spatial query will take longer than providing the reach_id. It is recommended that the reach_id be used instead once it is known.", "type": "number", "format": "float"}, {"name": "lon", "in": "query", "description": "A given longitude used to find the closest stream reach. Must be used together with `lat`. The spatial query uses the center point of the stream reach to determine proximity. This parameter can be given instead of providing `reach_id` in the path. A lat/lon spatial query will take longer than providing the reach_id. It is recommended that the reach_id be used instead once it is known.", "type": "number", "format": "float"}, {"name": "date", "in": "query", "description": "The given date for the forecast of interest given as YYYYMMDD (e.g. 20201020). If left blank it defaults to the most recent date. This API provides access to data within the last 30 days.", "type": "string", "pattern": "^[0-9]{4}(0[1-9]|1[0-2])(0[1-9]|[1-2][0-9]|3[0-1])(.(00|12)|)$"}, {"name": "ensemble", "in": "query", "description": "The desired forecast ensemble(s). Input can be:\n - An individual ensemble (i.e. 5)\n - A group of ensembles separated by commas (i.e. 1,5,9)\n - A range of ensembles separated by a dash (i.e. 3-15)\n - A combination of individual numbers and ranges (i.e. 1,5,7-16,24-35)", "type": "string", "default": "all"}, {"name": "return_format", "in": "query", "description": "The format of the time series response.", "type": "string", "default": "csv", "enum": ["csv", "json"]}], "produces": ["text/csv", "application/json"], "responses": {"200": {"description": "The response body will contain a time series for each ensemble along with metadata about the stream reach of interest."}, "400": {"description": "Bad request. Check request and paramaters.", "examples": {"error": "An unexpected error occured."}}, "422": {"description": "Missing a required parameter.", "examples": {"error": "region is required as input."}}}}}, "/ForecastRecords": {"get": {"description": "This retrieves the rolling record of the mean of the forecasted streamflow during the first 24 hours of each day's forecast. That is, each day day after the\nstreamflow forecasts are computed, the average of first 8 of the 3-hour timesteps are recorded to a csv. This retrieves that rolling record", "summary": "Return rolling record of average flows", "parameters": [{"name": "lat", "in": "query", "description": "A given latitude used to find the closest stream reach. Must be used together with `lon`. The spatial query uses the center point of the stream reach to determine proximity. This parameter can be given instead of providing `reach_id` in the path. A lat/lon spatial query will take longer than providing the reach_id. It is recommended that the reach_id be used instead once it is known.", "type": "number", "format": "float"}, {"name": "lon", "in": "query", "description": "A given longitude used to find the closest stream reach. Must be used together with `lat`. The spatial query uses the center point of the stream reach to determine proximity. This parameter can be given instead of providing `reach_id` in the path. A lat/lon spatial query will take longer than providing the reach_id. It is recommended that the reach_id be used instead once it is known.", "type": "number", "format": "float"}, {"name": "start_date", "in": "query", "description": "A date in YYYYMMDD format when you would like to start retrieving data (if available). Defaults to Jan 1 of the current year.", "type": "string", "pattern": "^[0-9]{4}(0[1-9]|1[0-2])(0[1-9]|[1-2][0-9]|3[0-1])$"}, {"name": "end_date", "in": "query", "description": "A date in YYYYMMDD format when you would like to stop retrieving data (if available). Defaults to Dec 31 of the current year.", "type": "string", "pattern": "^[0-9]{4}(0[1-9]|1[0-2])(0[1-9]|[1-2][0-9]|3[0-1])$"}], "produces": ["text/csv"], "responses": {"200": {"description": "The response body will contain a time series for the specified stream reach"}, "400": {"description": "Bad request. Check request and paramaters.", "examples": {"error": "An unexpected error occured."}}, "422": {"description": "Missing a required parameter.", "examples": {"error": "region is required as input."}}}}}, "/Hindcast": {"get": {"description": "This operation returns simulated streamflow data based on the ERA-5 dataset. A successful response will return a time series with date-value pairs.", "summary": "Return historic simulation", "parameters": [{"name": "lat", "in": "query", "description": "A given latitude used to find the closest stream reach. Must be used together with `lon`. The spatial query uses the center point of the stream reach to determine proximity. This parameter can be given instead of providing `reach_id` in the path. A lat/lon spatial query will take longer than providing the reach_id. It is recommended that the reach_id be used instead once it is known.", "type": "number", "format": "float"}, {"name": "lon", "in": "query", "description": "A given longitude used to find the closest stream reach. Must be used together with `lat`. The spatial query uses the center point of the stream reach to determine proximity. This parameter can be given instead of providing `reach_id` in the path. A lat/lon spatial query will take longer than providing the reach_id. It is recommended that the reach_id be used instead once it is known.", "type": "number", "format": "float"}, {"name": "return_format", "in": "query", "description": "The format of the time series response.", "type": "string", "default": "csv", "enum": ["csv", "json"]}], "produces": ["text/csv", "application/json"], "responses": {"200": {"description": "The response body will contain a time series along with metadata about the stream reach of interest."}, "400": {"description": "Bad request. Check request and paramaters.", "examples": {"error": "An unexpected error occured."}}, "422": {"description": "Missing a required parameter.", "examples": {"error": "region is required as input."}}}}}, "/DailyAverages": {"get": {"description": "This operation returns the average flow for each day of the year for the Historic Simulation", "summary": "Return historic simulation's daily averages", "parameters": [{"name": "lat", "in": "query", "description": "A given latitude used to find the closest stream reach. Must be used together with `lon`. The spatial query uses the center point of the stream reach to determine proximity. This parameter can be given instead of providing `reach_id` in the path. A lat/lon spatial query will take longer than providing the reach_id. It is recommended that the reach_id be used instead once it is known.", "type": "number", "format": "float"}, {"name": "lon", "in": "query", "description": "A given longitude used to find the closest stream reach. Must be used together with `lat`. The spatial query uses the center point of the stream reach to determine proximity. This parameter can be given instead of providing `reach_id` in the path. A lat/lon spatial query will take longer than providing the reach_id. It is recommended that the reach_id be used instead once it is known.", "type": "number", "format": "float"}, {"name": "return_format", "in": "query", "description": "The format of the time series response.", "type": "string", "default": "csv", "enum": ["csv", "json"]}], "produces": ["text/csv", "application/json"], "responses": {"200": {"description": "The response body will contain a time series along with metadata about the stream reach of interest."}, "400": {"description": "Bad request. Check request and paramaters.", "examples": {"error": "An unexpected error occured."}}, "422": {"description": "Missing a required parameter.", "examples": {"error": "region is required as input."}}}}}, "/MonthlyAverages": {"get": {"description": "This operation returns the average flow for each month of the year for the Historic Simulation", "summary": "Return historic simulation's monthly averages", "parameters": [{"name": "lat", "in": "query", "description": "A given latitude used to find the closest stream reach. Must be used together with `lon`. The spatial query uses the center point of the stream reach to determine proximity. This parameter can be given instead of providing `reach_id` in the path. A lat/lon spatial query will take longer than providing the reach_id. It is recommended that the reach_id be used instead once it is known.", "type": "number", "format": "float"}, {"name": "lon", "in": "query", "description": "A given longitude used to find the closest stream reach. Must be used together with `lat`. The spatial query uses the center point of the stream reach to determine proximity. This parameter can be given instead of providing `reach_id` in the path. A lat/lon spatial query will take longer than providing the reach_id. It is recommended that the reach_id be used instead once it is known.", "type": "number", "format": "float"}, {"name": "return_format", "in": "query", "description": "The format of the time series response.", "type": "string", "default": "csv", "enum": ["csv", "json"]}], "produces": ["text/csv", "application/json"], "responses": {"200": {"description": "The response body will contain a time series along with metadata about the stream reach of interest."}, "400": {"description": "Bad request. Check request and paramaters.", "examples": {"error": "An unexpected error occured."}}, "422": {"description": "Missing a required parameter.", "examples": {"error": "region is required as input."}}}}}, "/ReturnPeriods": {"get": {"description": "This operation returns the 2, 5, 10, 25, 50, and 100 year return period based on the 40-years simulated streamflow data and using the Gumbel Method. A successful response will return key-value pairs for earch return period along with metadata.", "summary": "Return historic simulation", "parameters": [{"name": "lat", "in": "query", "description": "A given latitude used to find the closest stream reach. Must be used together with `lon`. The spatial query uses the center point of the stream reach to determine proximity. This parameter can be given instead of providing `reach_id` in the path. A lat/lon spatial query will take longer than providing the reach_id. It is recommended that the reach_id be used instead once it is known.", "type": "number", "format": "float"}, {"name": "lon", "in": "query", "description": "A given longitude used to find the closest stream reach. Must be used together with `lat`. The spatial query uses the center point of the stream reach to determine proximity. This parameter can be given instead of providing `reach_id` in the path. A lat/lon spatial query will take longer than providing the reach_id. It is recommended that the reach_id be used instead once it is known.", "type": "number", "format": "float"}, {"name": "return_format", "in": "query", "description": "The format of the time series response.", "type": "string", "default": "csv", "enum": ["csv", "json"]}], "produces": ["text/csv", "application/json"], "responses": {"200": {"description": "The response body will contain a key-value pairs for each return period along with metadata about the stream reach of interest."}, "400": {"description": "Bad request. Check request and paramaters.", "examples": {"error": "An unexpected error occured."}}, "422": {"description": "Missing a required parameter.", "examples": {"error": "region is required as input."}}}}}}};
const spec = {"swagger": "2.0", "info": {"title": "GEOGLOWS ECMWF Streamflow Data Service", "description": "A REST Data Service to access high resolution streamflow forecasts from around the world", "version": "2.0.0"}, "host": "geoglows.ecmwf.int", "basePath": "/api", "schemes": ["https", "http"], "paths": {"/Hydroviewer/{reach_id}/{return_format}": {"get": {"description": "A shorthand for retrieving the forecast records and stats, and return periods, usually all plotted together.", "summary": "Returns forecast records, forecast stats, and return periods.", "parameters": [{"name": "reach_id", "in": "path", "description": "The stream reach's unique ID also refered to as common identifier (COMID). If the ID is not known, use the getReachID method.", "type": "number", "format": "integer", "required": true}, {"name": "return_format", "in": "path", "required": true, "description": "The format of the response", "type": "string", "default": "csv", "enum": ["csv", "json"]}, {"name": "date", "in": "query", "description": "The given date for the forecast of interest given as YYYYMMDD (e.g. 20201020). If left blank it defaults to the most recent date. This API provides access to data within the last 30 days.", "type": "string", "pattern": "^[0-9]{4}(0[1-9]|1[0-2])(0[1-9]|[1-2][0-9]|3[0-1])(.(00|12)|)$"}], "produces": ["text/csv", "application/json"], "responses": {"200": {"description": "The response body will contain a time series along with metadata about the stream reach of interest."}, "400": {"description": "Bad request. Check request and paramaters.", "examples": {"error": "An unexpected error occured."}}, "422": {"description": "Missing a required parameter.", "examples": {"error": "region is required as input."}}}}}, "/Forecast/{reach_id}/{return_format}": {"get": {"description": "This operation returns the average of the ensemble forecast.", "summary": "Returns average forecasted flow", "parameters": [{"name": "reach_id", "in": "path", "description": "The stream reach's unique ID also refered to as common identifier (COMID). If the ID is not known, use the getReachID method.", "type": "number", "format": "integer", "required": true}, {"name": "return_format", "in": "path", "required": true, "description": "The format of the response", "type": "string", "default": "csv", "enum": ["csv", "json"]}, {"name": "date", "in": "query", "description": "The given date for the forecast of interest given as YYYYMMDD (e.g. 20201020). If left blank it defaults to the most recent date. This API provides access to data within the last 30 days.", "type": "string", "pattern": "^[0-9]{4}(0[1-9]|1[0-2])(0[1-9]|[1-2][0-9]|3[0-1])(.(00|12)|)$"}], "produces": ["text/csv", "application/json"], "responses": {"200": {"description": "The response body will contain a time series along with metadata about the stream reach of interest."}, "400": {"description": "Bad request. Check request and paramaters.", "examples": {"error": "An unexpected error occured."}}, "422": {"description": "Missing a required parameter.", "examples": {"error": "region is required as input."}}}}}, "/ForecastStats/{reach_id}/{return_format}": {"get": {"description": "This operation returns statistics calculated from 51 forecast ensemble members. A successful response will return a time series with date-value pairs.", "summary": "Return basic forecast statistics", "parameters": [{"name": "reach_id", "in": "path", "description": "The stream reach's unique ID also refered to as common identifier (COMID). If the ID is not known, use the getReachID method.", "type": "number", "format": "integer", "required": true}, {"name": "return_format", "in": "path", "required": true, "description": "The format of the response", "type": "string", "default": "csv", "enum": ["csv", "json"]}, {"name": "date", "in": "query", "description": "The given date for the forecast of interest given as YYYYMMDD (e.g. 20201020). If left blank it defaults to the most recent date. This API provides access to data within the last 30 days.", "type": "string", "pattern": "^[0-9]{4}(0[1-9]|1[0-2])(0[1-9]|[1-2][0-9]|3[0-1])(.(00|12)|)$"}], "produces": ["text/csv", "application/json"], "responses": {"200": {"description": "The response body will contain a time series along with metadata about the stream reach of interest."}, "400": {"description": "Bad request. Check request and paramaters.", "examples": {"error": "An unexpected error occured."}}, "422": {"description": "Missing a required parameter.", "examples": {"error": "region is required as input."}}}}}, "/ForecastEnsembles/{reach_id}/{return_format}": {"get": {"description": "This operation returns a timeseries for each of the 51 normal forecast ensemble members and the 52nd higher resolution forecast. A successful response will return a time series with date-value pairs.", "summary": "Return forecast ensembles", "parameters": [{"name": "reach_id", "in": "path", "description": "The stream reach's unique ID also refered to as common identifier (COMID). If the ID is not known, use the getReachID method.", "type": "number", "format": "integer", "required": true}, {"name": "date", "in": "query", "description": "The given date for the forecast of interest given as YYYYMMDD (e.g. 20201020). If left blank it defaults to the most recent date. This API provides access to data within the last 30 days.", "type": "string", "pattern": "^[0-9]{4}(0[1-9]|1[0-2])(0[1-9]|[1-2][0-9]|3[0-1])(.(00|12)|)$"}, {"name": "ensemble", "in": "query", "description": "The desired forecast ensemble(s). Input can be:\n - An individual ensemble (i.e. 5)\n - A group of ensembles separated by commas (i.e. 1,5,9)\n - A range of ensembles separated by a dash (i.e. 3-15)\n - A combination of individual numbers and ranges (i.e. 1,5,7-16,24-35)", "type": "string", "default": "all"}, {"name": "return_format", "in": "path", "required": true, "description": "The format of the response", "type": "string", "default": "csv", "enum": ["csv", "json"]}], "produces": ["text/csv", "application/json"], "responses": {"200": {"description": "The response body will contain a time series for each ensemble along with metadata about the stream reach of interest."}, "400": {"description": "Bad request. Check request and paramaters.", "examples": {"error": "An unexpected error occured."}}, "422": {"description": "Missing a required parameter.", "examples": {"error": "region is required as input."}}}}}, "/ForecastRecords/{reach_id}/{return_format}": {"get": {"description": "This retrieves the rolling record of the mean of the forecasted streamflow during the first 24 hours of each day's forecast. That is, each day day after the\nstreamflow forecasts are computed, the average of first 8 of the 3-hour timesteps are recorded to a csv. This retrives that rolling record", "summary": "Return rolling record of average flows", "parameters": [{"name": "reach_id", "in": "path", "description": "The stream reach's unique ID also refered to as common identifier (COMID). If the ID is not known, use the getReachID method.", "type": "number", "format": "integer", "required": true}, {"name": "start_date", "in": "query", "description": "A date in YYYYMMDD format when you would like to start retrieving data (if available). Defaults to Jan 1 of the current year.", "type": "string", "pattern": "^[0-9]{4}(0[1-9]|1[0-2])(0[1-9]|[1-2][0-9]|3[0-1])$"}, {"name": "end_date", "in": "query", "description": "A date in YYYYMMDD format when you would like to stop retrieving data (if available). Defaults to Dec 31 of the current year.", "type": "string", "pattern": "^[0-9]{4}(0[1-9]|1[0-2])(0[1-9]|[1-2][0-9]|3[0-1])$"}], "produces": ["text/csv", "application/json"], "responses": {"200": {"description": "The response body will contain a time series for the specified stream reach"}, "400": {"description": "Bad request. Check request and paramaters.", "examples": {"error": "An unexpected error occured."}}, "422": {"description": "Missing a required parameter.", "examples": {"error": "region is required as input."}}}}}, "/ForecastAnomalies/{reach_id}/{return_format}": {"get": {"description": "Computes the deviations from the daily averages", "summary": "Calculates the differences between the daily average and the forecasted flow for each day", "parameters": [{"name": "reach_id", "in": "path", "description": "The stream reach's unique ID also refered to as common identifier (COMID). If the ID is not known, use the getReachID method.", "type": "number", "format": "integer", "required": true}, {"name": "start_date", "in": "query", "description": "A date in YYYYMMDD format when you would like to start retrieving data (if available). Defaults to Jan 1 of the current year.", "type": "string", "pattern": "^[0-9]{4}(0[1-9]|1[0-2])(0[1-9]|[1-2][0-9]|3[0-1])$"}, {"name": "end_date", "in": "query", "description": "A date in YYYYMMDD format when you would like to stop retrieving data (if available). Defaults to Dec 31 of the current year.", "type": "string", "pattern": "^[0-9]{4}(0[1-9]|1[0-2])(0[1-9]|[1-2][0-9]|3[0-1])$"}], "produces": ["text/csv", "application/json"], "responses": {"200": {"description": "The response body will contain a time series for the specified stream reach"}, "400": {"description": "Bad request. Check request and paramaters.", "examples": {"error": "An unexpected error occured."}}, "422": {"description": "Missing a required parameter.", "examples": {"error": "region is required as input."}}}}}, "/ForecastWarnings/{return_format}": {"get": {"description": "This method returns a csv created to summarize the potential return period level flow events coming to the reaches in a specified region. The CSV contains a column for the reach_id, lat of the reach, lon of the reach, maximum forecasted flow in the next 15 day forecast, and a column for each of the return periods (2, 10, 20, 25, 50, 100) which will contain the date when the forecast is first expected to pass that threshold.", "summary": "Returns potential warnings for the forecast", "parameters": [{"name": "region", "in": "path", "description": "the name of one of the regions that the streamflow forecasts are generated for. You can find this with the AvailableRegions method.", "type": "string", "default": "all", "required": true}, {"name": "forecast_date", "in": "query", "description": "If multiple forecast dates are available, provide the date in the format YYYYMMDD.HH of the forecast you would like to query. Use the AvailableDates method if you need to find out which dates are available. Defaults to the most recent available data.", "type": "string", "format": "string"}, {"name": "return_format", "in": "path", "required": true, "description": "The format of the response", "type": "string", "default": "csv", "enum": ["csv", "json"]}], "produces": ["text/csv", "application/json"], "responses": {"200": {"description": "The response body will contain a csv table with the information previous mentioned."}, "400": {"description": "Bad request. Check request and paramaters.", "examples": {"error": "An unexpected error occured."}}, "422": {"description": "Missing a required parameter.", "examples": {"error": "region or both lat and lon are required as input"}}}}}, "/ForecastDates": {"get": {"description": "This operation returns the available forecast dates in JSON format.", "summary": "Available dates", "produces": ["application/json"], "responses": {"200": {"description": "The response body will contain a list of available dates."}, "204": {"description": "Succesful request but no regions found.", "examples": {"message": "No dates available."}}, "400": {"description": "Bad request. Check request and paramaters.", "examples": {"error": "An unexpected error occured."}}, "422": {"description": "Missing a required parameter.", "examples": {"error": "region is required as input."}}}}}, "/Hindcast/{reach_id}/{return_format}": {"get": {"description": "This operation returns simulated streamflow data based on the ERA-5 dataset. A successful response will return a time series with date-value pairs.", "summary": "Return historic simulation", "parameters": [{"name": "reach_id", "in": "path", "description": "The stream reach's unique ID also refered to as common identifier (COMID). If the ID is not known, use the getReachID method.", "type": "number", "format": "integer", "required": true}, {"name": "return_format", "in": "path", "required": true, "description": "The format of the response", "type": "string", "default": "csv", "enum": ["csv", "json"]}], "produces": ["text/csv", "application/json"], "responses": {"200": {"description": "The response body will contain a time series along with metadata about the stream reach of interest."}, "400": {"description": "Bad request. Check request and paramaters.", "examples": {"error": "An unexpected error occured."}}, "422": {"description": "Missing a required parameter.", "examples": {"error": "region is required as input."}}}}}, "/DailyAverages/{reach_id}/{return_format}": {"get": {"description": "This operation returns the average flow for each day of the year for the Historic Simulation", "summary": "Return historic simulation's daily averages", "parameters": [{"name": "reach_id", "in": "path", "description": "The stream reach's unique ID also refered to as common identifier (COMID). If the ID is not known, use the getReachID method.", "type": "number", "format": "integer", "required": true}, {"name": "return_format", "in": "path", "required": true, "description": "The format of the response", "type": "string", "default": "csv", "enum": ["csv", "json"]}], "produces": ["text/csv", "application/json"], "responses": {"200": {"description": "The response body will contain a time series along with metadata about the stream reach of interest."}, "400": {"description": "Bad request. Check request and paramaters.", "examples": {"error": "An unexpected error occured."}}, "422": {"description": "Missing a required parameter.", "examples": {"error": "region is required as input."}}}}}, "/MonthlyAverages/{reach_id}/{return_format}": {"get": {"description": "This operation returns the average flow for each month of the year for the Historic Simulation", "summary": "Return historic simulation's monthly averages", "parameters": [{"name": "reach_id", "in": "path", "description": "The stream reach's unique ID also refered to as common identifier (COMID). If the ID is not known, use the getReachID method.", "type": "number", "format": "integer", "required": true}, {"name": "return_format", "in": "path", "required": true, "description": "The format of the response", "type": "string", "default": "csv", "enum": ["csv", "json"]}], "produces": ["text/csv", "application/json"], "responses": {"200": {"description": "The response body will contain a time series along with metadata about the stream reach of interest."}, "400": {"description": "Bad request. Check request and paramaters.", "examples": {"error": "An unexpected error occured."}}, "422": {"description": "Missing a required parameter.", "examples": {"error": "region is required as input."}}}}}, "/ReturnPeriods/{reach_id}/{return_format}": {"get": {"description": "This operation returns the 2, 5, 10, 25, 50, and 100 year return period based on the 40-years simulated streamflow data and using the Gumbel Method. A successful response will return key-value pairs for earch return period along with metadata.", "summary": "Return historic simulation", "parameters": [{"name": "reach_id", "in": "path", "description": "The stream reach's unique ID also refered to as common identifier (COMID). If the ID is not known, use the getReachID method.", "type": "number", "format": "integer", "required": true}, {"name": "return_format", "in": "path", "required": true, "description": "The format of the response", "type": "string", "default": "csv", "enum": ["csv", "json"]}], "produces": ["text/csv", "application/json"], "responses": {"200": {"description": "The response body will contain a key-value pairs for each return period along with metadata about the stream reach of interest."}, "400": {"description": "Bad request. Check request and paramaters.", "examples": {"error": "An unexpected error occured."}}, "422": {"description": "Missing a required parameter.", "examples": {"error": "region is required as input."}}}}}, "/getReachID": {"get": {"description": "Find the Reach ID nearest a point using latitude and longitude coordinates", "summary": "Find the Reach ID nearest a point using latitude and longitude coordinates", "parameters": [{"name": "lat", "in": "query", "required": true, "description": "The latitude of a point to search", "type": "number", "format": "float"}, {"name": "lon", "in": "query", "required": true, "description": "The longitude of a point to search", "type": "number", "format": "float"}], "produces": ["application/json"], "responses": {"200": {"description": null}, "400": {"description": "Bad request. Check request and paramaters.", "examples": {"error": "An unexpected error occured."}}, "422": {"description": "Missing a required parameter.", "examples": {"error": "region is required as input."}}}}}, "/ForecastStats": {"get": {"description": "This operation returns statistics calculated from 51 forecast ensemble members. A successful response will return a time series with date-value pairs.", "summary": "Return basic forecast statistics (if unable to provide reach_id, lat and lon can be provided as query arguments)", "parameters": [{"name": "lat", "in": "query", "description": "A given latitude used to find the closest stream reach. Must be used together with `lon`. The spatial query uses the center point of the stream reach to determine proximity. This parameter can be given instead of providing `reach_id` in the path. A lat/lon spatial query will take longer than providing the reach_id. It is recommended that the reach_id be used instead once it is known.", "type": "number", "format": "float"}, {"name": "lon", "in": "query", "description": "A given longitude used to find the closest stream reach. Must be used together with `lat`. The spatial query uses the center point of the stream reach to determine proximity. This parameter can be given instead of providing `reach_id` in the path. A lat/lon spatial query will take longer than providing the reach_id. It is recommended that the reach_id be used instead once it is known.", "type": "number", "format": "float"}, {"name": "date", "in": "query", "description": "The given date for the forecast of interest given as YYYYMMDD (e.g. 20201020). If left blank it defaults to the most recent date. This API provides access to data within the last 30 days.", "type": "string", "pattern": "^[0-9]{4}(0[1-9]|1[0-2])(0[1-9]|[1-2][0-9]|3[0-1])(.(00|12)|)$"}, {"name": "return_format", "in": "query", "description": "The format of the time series response.", "type": "string", "default": "csv", "enum": ["csv", "json"]}], "produces": ["text/csv", "application/json"], "responses": {"200": {"description": "The response body will contain a time series along with metadata about the stream reach of interest."}, "400": {"description": "Bad request. Check request and paramaters.", "examples": {"error": "An unexpected error occured."}}, "422": {"description": "Missing a required parameter.", "examples": {"error": "region is required as input."}}}}}, "/ForecastEnsembles": {"get": {"description": "This operation returns a timeseries for each of the 51 normal forecast ensemble members and the 52nd higher resolution forecast. A successful response will return a time series with date-value pairs.", "summary": "Return forecast ensembles", "parameters": [{"name": "lat", "in": "query", "description": "A given latitude used to find the closest stream reach. Must be used together with `lon`. The spatial query uses the center point of the stream reach to determine proximity. This parameter can be given instead of providing `reach_id` in the path. A lat/lon spatial query will take longer than providing the reach_id. It is recommended that the reach_id be used instead once it is known.", "type": "number", "format": "float"}, {"name": "lon", "in": "query", "description": "A given longitude used to find the closest stream reach. Must be used together with `lat`. The spatial query uses the center point of the stream reach to determine proximity. This parameter can be given instead of providing `reach_id` in the path. A lat/lon spatial query will take longer than providing the reach_id. It is recommended that the reach_id be used instead once it is known.", "type": "number", "format": "float"}, {"name": "date", "in": "query", "description": "The given date for the forecast of interest given as YYYYMMDD (e.g. 20201020). If left blank it defaults to the most recent date. This API provides access to data within the last 30 days.", "type": "string", "pattern": "^[0-9]{4}(0[1-9]|1[0-2])(0[1-9]|[1-2][0-9]|3[0-1])(.(00|12)|)$"}, {"name": "ensemble", "in": "query", "description": "The desired forecast ensemble(s). Input can be:\n - An individual ensemble (i.e. 5)\n - A group of ensembles separated by commas (i.e. 1,5,9)\n - A range of ensembles separated by a dash (i.e. 3-15)\n - A combination of individual numbers and ranges (i.e. 1,5,7-16,24-35)", "type": "string", "default": "all"}, {"name": "return_format", "in": "query", "description": "The format of the time series response.", "type": "string", "default": "csv", "enum": ["csv", "json"]}], "produces": ["text/csv", "application/json"], "responses": {"200": {"description": "The response body will contain a time series for each ensemble along with metadata about the stream reach of interest."}, "400": {"description": "Bad request. Check request and paramaters.", "examples": {"error": "An unexpected error occured."}}, "422": {"description": "Missing a required parameter.", "examples": {"error": "region is required as input."}}}}}, "/ForecastRecords": {"get": {"description": "This retrieves the rolling record of the mean of the forecasted streamflow during the first 24 hours of each day's forecast. That is, each day day after the\nstreamflow forecasts are computed, the average of first 8 of the 3-hour timesteps are recorded to a csv. This retrieves that rolling record", "summary": "Return rolling record of average flows", "parameters": [{"name": "lat", "in": "query", "description": "A given latitude used to find the closest stream reach. Must be used together with `lon`. The spatial query uses the center point of the stream reach to determine proximity. This parameter can be given instead of providing `reach_id` in the path. A lat/lon spatial query will take longer than providing the reach_id. It is recommended that the reach_id be used instead once it is known.", "type": "number", "format": "float"}, {"name": "lon", "in": "query", "description": "A given longitude used to find the closest stream reach. Must be used together with `lat`. The spatial query uses the center point of the stream reach to determine proximity. This parameter can be given instead of providing `reach_id` in the path. A lat/lon spatial query will take longer than providing the reach_id. It is recommended that the reach_id be used instead once it is known.", "type": "number", "format": "float"}, {"name": "start_date", "in": "query", "description": "A date in YYYYMMDD format when you would like to start retrieving data (if available). Defaults to Jan 1 of the current year.", "type": "string", "pattern": "^[0-9]{4}(0[1-9]|1[0-2])(0[1-9]|[1-2][0-9]|3[0-1])$"}, {"name": "end_date", "in": "query", "description": "A date in YYYYMMDD format when you would like to stop retrieving data (if available). Defaults to Dec 31 of the current year.", "type": "string", "pattern": "^[0-9]{4}(0[1-9]|1[0-2])(0[1-9]|[1-2][0-9]|3[0-1])$"}], "produces": ["text/csv"], "responses": {"200": {"description": "The response body will contain a time series for the specified stream reach"}, "400": {"description": "Bad request. Check request and paramaters.", "examples": {"error": "An unexpected error occured."}}, "422": {"description": "Missing a required parameter.", "examples": {"error": "region is required as input."}}}}}, "/Hindcast": {"get": {"description": "This operation returns simulated streamflow data based on the ERA-5 dataset. A successful response will return a time series with date-value pairs.", "summary": "Return historic simulation", "parameters": [{"name": "lat", "in": "query", "description": "A given latitude used to find the closest stream reach. Must be used together with `lon`. The spatial query uses the center point of the stream reach to determine proximity. This parameter can be given instead of providing `reach_id` in the path. A lat/lon spatial query will take longer than providing the reach_id. It is recommended that the reach_id be used instead once it is known.", "type": "number", "format": "float"}, {"name": "lon", "in": "query", "description": "A given longitude used to find the closest stream reach. Must be used together with `lat`. The spatial query uses the center point of the stream reach to determine proximity. This parameter can be given instead of providing `reach_id` in the path. A lat/lon spatial query will take longer than providing the reach_id. It is recommended that the reach_id be used instead once it is known.", "type": "number", "format": "float"}, {"name": "return_format", "in": "query", "description": "The format of the time series response.", "type": "string", "default": "csv", "enum": ["csv", "json"]}], "produces": ["text/csv", "application/json"], "responses": {"200": {"description": "The response body will contain a time series along with metadata about the stream reach of interest."}, "400": {"description": "Bad request. Check request and paramaters.", "examples": {"error": "An unexpected error occured."}}, "422": {"description": "Missing a required parameter.", "examples": {"error": "region is required as input."}}}}}, "/DailyAverages": {"get": {"description": "This operation returns the average flow for each day of the year for the Historic Simulation", "summary": "Return historic simulation's daily averages", "parameters": [{"name": "lat", "in": "query", "description": "A given latitude used to find the closest stream reach. Must be used together with `lon`. The spatial query uses the center point of the stream reach to determine proximity. This parameter can be given instead of providing `reach_id` in the path. A lat/lon spatial query will take longer than providing the reach_id. It is recommended that the reach_id be used instead once it is known.", "type": "number", "format": "float"}, {"name": "lon", "in": "query", "description": "A given longitude used to find the closest stream reach. Must be used together with `lat`. The spatial query uses the center point of the stream reach to determine proximity. This parameter can be given instead of providing `reach_id` in the path. A lat/lon spatial query will take longer than providing the reach_id. It is recommended that the reach_id be used instead once it is known.", "type": "number", "format": "float"}, {"name": "return_format", "in": "query", "description": "The format of the time series response.", "type": "string", "default": "csv", "enum": ["csv", "json"]}], "produces": ["text/csv", "application/json"], "responses": {"200": {"description": "The response body will contain a time series along with metadata about the stream reach of interest."}, "400": {"description": "Bad request. Check request and paramaters.", "examples": {"error": "An unexpected error occured."}}, "422": {"description": "Missing a required parameter.", "examples": {"error": "region is required as input."}}}}}, "/MonthlyAverages": {"get": {"description": "This operation returns the average flow for each month of the year for the Historic Simulation", "summary": "Return historic simulation's monthly averages", "parameters": [{"name": "lat", "in": "query", "description": "A given latitude used to find the closest stream reach. Must be used together with `lon`. The spatial query uses the center point of the stream reach to determine proximity. This parameter can be given instead of providing `reach_id` in the path. A lat/lon spatial query will take longer than providing the reach_id. It is recommended that the reach_id be used instead once it is known.", "type": "number", "format": "float"}, {"name": "lon", "in": "query", "description": "A given longitude used to find the closest stream reach. Must be used together with `lat`. The spatial query uses the center point of the stream reach to determine proximity. This parameter can be given instead of providing `reach_id` in the path. A lat/lon spatial query will take longer than providing the reach_id. It is recommended that the reach_id be used instead once it is known.", "type": "number", "format": "float"}, {"name": "return_format", "in": "query", "description": "The format of the time series response.", "type": "string", "default": "csv", "enum": ["csv", "json"]}], "produces": ["text/csv", "application/json"], "responses": {"200": {"description": "The response body will contain a time series along with metadata about the stream reach of interest."}, "400": {"description": "Bad request. Check request and paramaters.", "examples": {"error": "An unexpected error occured."}}, "422": {"description": "Missing a required parameter.", "examples": {"error": "region is required as input."}}}}}, "/ReturnPeriods": {"get": {"description": "This operation returns the 2, 5, 10, 25, 50, and 100 year return period based on the 40-years simulated streamflow data and using the Gumbel Method. A successful response will return key-value pairs for earch return period along with metadata.", "summary": "Return historic simulation", "parameters": [{"name": "lat", "in": "query", "description": "A given latitude used to find the closest stream reach. Must be used together with `lon`. The spatial query uses the center point of the stream reach to determine proximity. This parameter can be given instead of providing `reach_id` in the path. A lat/lon spatial query will take longer than providing the reach_id. It is recommended that the reach_id be used instead once it is known.", "type": "number", "format": "float"}, {"name": "lon", "in": "query", "description": "A given longitude used to find the closest stream reach. Must be used together with `lat`. The spatial query uses the center point of the stream reach to determine proximity. This parameter can be given instead of providing `reach_id` in the path. A lat/lon spatial query will take longer than providing the reach_id. It is recommended that the reach_id be used instead once it is known.", "type": "number", "format": "float"}, {"name": "return_format", "in": "query", "description": "The format of the time series response.", "type": "string", "default": "csv", "enum": ["csv", "json"]}], "produces": ["text/csv", "application/json"], "responses": {"200": {"description": "The response body will contain a key-value pairs for each return period along with metadata about the stream reach of interest."}, "400": {"description": "Bad request. Check request and paramaters.", "examples": {"error": "An unexpected error occured."}}, "422": {"description": "Missing a required parameter.", "examples": {"error": "region is required as input."}}}}}}};
Copy link

Choose a reason for hiding this comment

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

The Swagger specification is using version 2.0. Consider upgrading to OpenAPI 3.0 for enhanced features and improved support for API documentation. Additionally, ensure that the inclusion of both HTTP and HTTPS schemes is intentional and that appropriate security measures are in place for HTTP connections.

Comment on lines 1 to 2
PATH_TO_FORECASTS = '/mnt/output/forecasts'
PATH_TO_FORECAST_RECORDS = '/mnt/output/forecast-records'
Copy link

Choose a reason for hiding this comment

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

Hardcoded paths to forecasts and forecast records may limit flexibility in different deployment environments. Consider making these paths configurable through environment variables.

Comment on lines 6 to 7
ODP_S3_BUCKET_URI = 's3://geoglows-v2'
ODP_S3_BUCKET_REGION = 'us-west-2'
Copy link

Choose a reason for hiding this comment

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

Hardcoded S3 bucket URI and region may expose sensitive information and limit flexibility. Consider making these details configurable through environment variables.

Comment on lines +12 to +22
def get_reach_id(lat: float, lon: float, *, format: str = 'csv'):
"""
Finds the reach ID nearest to a given lat/lon
Uses the ModelMasterTable to find the locations
"""
reach_id, dist_error = latlon_to_reach(lat, lon)
if format == 'csv':
return df_to_csv_flask_response(pd.DataFrame(dict(reach_id=[reach_id, ], dist_error=[dist_error, ])),
csv_name='reach_id', index=False)
elif format == 'json':
return jsonify(dict(reach_id=reach_id, dist_error=dist_error))
Copy link

Choose a reason for hiding this comment

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

The get_reach_id function lacks input validation for lat and lon parameters. Consider adding checks to ensure they are within valid ranges for latitude (-90 to 90) and longitude (-180 to 180).

Comment on lines +27 to +47
def log_request(version: str, product: str, reach_id: int, return_format: str, source: str):
log_message = {
'version': version,
'product': product.lower().replace(" ", ""),
'reach_id': reach_id,
'return_format': return_format,
'source': source,
}

# Send the log message to CloudWatch
response = client.put_log_events(
logGroupName=LOG_GROUP_NAME,
logStreamName=LOG_STREAM_NAME,
logEvents=[
{
'timestamp': int(round(time.time() * 1000)),
'message': json.dumps(log_message)
}
]
)
return response
Copy link

Choose a reason for hiding this comment

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

The log_request function lacks error handling for potential failures in logging to CloudWatch Logs. Consider adding try-except blocks to gracefully handle exceptions and ensure the application's stability.

Comment on lines +20 to +60
@app.route(f'/api/<product>', methods=['GET'])
@app.route(f'/api/v1/<product>', methods=['GET'])
@cross_origin()
def rest_endpoints_v1(product: str):
log_request(version="v1",
product=product,
reach_id=request.args.get('reach_id', 0),
return_format=request.args.get('return_format', 'csv'),
source=request.args.get('source', 'other'), )

# forecast data products
if product == 'ForecastStats':
return forecast_stats(request)
elif product == 'ForecastEnsembles':
return forecast_ensembles(request)
elif product == 'ForecastWarnings':
return forecast_warnings(request)
elif product == 'ForecastRecords':
return forecast_records(request)

# historical data products
elif product == 'HistoricSimulation':
return historical(request)
elif product == 'ReturnPeriods':
return return_periods(request)
elif product == 'DailyAverages':
return historical_averages(request, 'daily')
elif product == 'MonthlyAverages':
return historical_averages(request, 'monthly')

elif product == 'AvailableData':
return get_available_data_handler()
elif product == 'AvailableRegions':
return get_region_handler()
elif product == 'AvailableDates':
return available_dates(request)
elif product == 'GetReachID':
return get_reach_id_from_latlon_handler(request)

else:
return jsonify({'status': f'data product "{product}" not available'}), 201
Copy link

Choose a reason for hiding this comment

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

The Flask Blueprint and routes for version 1 REST endpoints are correctly defined. Consider enhancing the error handling for unsupported products by providing more detailed information or suggestions for valid products. This can improve the API's usability and help consumers in diagnosing issues.

app/v2/data.py Outdated
Comment on lines 20 to 44
def get_forecast_dataset(reach_id: int, date: str) -> xr.Dataset:
"""
Opens the forecast dataset for a given date, selects the reach_id and Qout variable
"""
if date == "latest":
date = find_available_dates()[-1]

if len(date) == 8:
date = f"{date}00"

forecast_file = os.path.join(PATH_TO_FORECASTS, f'{date}.zarr')

if not os.path.exists(forecast_file):
raise ValueError(f'Data not found for date {date}. Use YYYYMMDD format and the AvailableDates endpoint.')

try:
forecast_dataset = xr.open_zarr(forecast_file)
except Exception as e:
print(e)
raise ValueError('Error while reading data from the zarr files')
try:
return forecast_dataset.sel(rivid=reach_id).Qout
except Exception as e:
print(e)
raise ValueError(f'Unable to get data for reach_id {reach_id} in the forecast dataset')
Copy link

Choose a reason for hiding this comment

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

The get_forecast_dataset function correctly retrieves forecast data. Consider adding more detailed error messages for cases where the data is not found or there's an error reading the zarr files. This can help in diagnosing issues more effectively.

Comment on lines +80 to +88
def latlon_to_reach(lat: float, lon: float) -> list:
"""
Finds the reach ID nearest to a given lat/lon
Uses the ModelMasterTable to find the locations
"""
df = pd.read_parquet('/mnt/configs/geoglows-v2-geographic-properties-table.parquet',
columns=['LINKNO', 'lat', 'lon'])
df['distance'] = ((df['lat'] - lat) ** 2 + (df['lon'] - lon) ** 2) ** 0.5
return df.sort_values('distance').reset_index(drop=True).iloc[0][['TDXHydroLinkNo', 'distance']].values.flatten()
Copy link

Choose a reason for hiding this comment

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

Tip

Codebase Verification

The geographic properties table is not accessible at the specified path (/mnt/configs/geoglows-v2-geographic-properties-table.parquet). This could potentially affect the functionality of the latlon_to_reach function, which relies on this table. It's important to ensure that the path is correct and accessible in the deployment environment.

Analysis chain

The latlon_to_reach function correctly finds the nearest reach ID to a given latitude and longitude. Verify that the path to the geographic properties table (/mnt/configs/geoglows-v2-geographic-properties-table.parquet) is accessible and correct in the deployment environment.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Verify the accessibility of the geographic properties table
if [ -f "/mnt/configs/geoglows-v2-geographic-properties-table.parquet" ]; then
    echo "Geographic properties table is accessible."
else
    echo "Geographic properties table is not accessible. Check the path."
fi

Length of output: 208

Comment on lines 30 to 48
def retrospective(reach_id: int, units: str, return_format: str, start_date: str = None,
end_date: str = None) -> pd.DataFrame:
"""
Controller for retrieving simulated historic data
"""
df = _get_retrospective_df(reach_id)

if units == 'cfs':
df *= M3_TO_FT3
if start_date is not None:
df = df.loc[df.index >= datetime.datetime.strptime(start_date, '%Y%m%d')]
if end_date is not None:
df = df.loc[df.index <= datetime.datetime.strptime(end_date, '%Y%m%d')]

if return_format == 'csv':
return df_to_csv_flask_response(df, f'retrospective_{reach_id}_{units}')
if return_format == 'json':
return df_to_jsonify_response(df=df, reach_id=reach_id, units=units)
return df
Copy link

Choose a reason for hiding this comment

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

The retrospective function correctly handles unit conversion and date filtering. Consider adding input validation for units and return_format to ensure they match expected values.

+ # Validate units and return_format
+ if units not in ['cfs', 'cms']:
+     raise ValueError("Invalid units. Expected 'cfs' or 'cms'.")
+ if return_format not in ['csv', 'json']:
+     raise ValueError("Invalid return_format. Expected 'csv' or 'json'.")

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
def retrospective(reach_id: int, units: str, return_format: str, start_date: str = None,
end_date: str = None) -> pd.DataFrame:
"""
Controller for retrieving simulated historic data
"""
df = _get_retrospective_df(reach_id)
if units == 'cfs':
df *= M3_TO_FT3
if start_date is not None:
df = df.loc[df.index >= datetime.datetime.strptime(start_date, '%Y%m%d')]
if end_date is not None:
df = df.loc[df.index <= datetime.datetime.strptime(end_date, '%Y%m%d')]
if return_format == 'csv':
return df_to_csv_flask_response(df, f'retrospective_{reach_id}_{units}')
if return_format == 'json':
return df_to_jsonify_response(df=df, reach_id=reach_id, units=units)
return df
def retrospective(reach_id: int, units: str, return_format: str, start_date: str = None,
end_date: str = None) -> pd.DataFrame:
"""
Controller for retrieving simulated historic data
"""
# Validate units and return_format
if units not in ['cfs', 'cms']:
raise ValueError("Invalid units. Expected 'cfs' or 'cms'.")
if return_format not in ['csv', 'json']:
raise ValueError("Invalid return_format. Expected 'csv' or 'json'.")
df = _get_retrospective_df(reach_id)
if units == 'cfs':
df *= M3_TO_FT3
if start_date is not None:
df = df.loc[df.index >= datetime.datetime.strptime(start_date, '%Y%m%d')]
if end_date is not None:
df = df.loc[df.index <= datetime.datetime.strptime(end_date, '%Y%m%d')]
if return_format == 'csv':
return df_to_csv_flask_response(df, f'retrospective_{reach_id}_{units}')
if return_format == 'json':
return df_to_jsonify_response(df=df, reach_id=reach_id, units=units)
return df

Comment on lines 28 to 81
@app.route(f'/api/latest/<product>/', methods=['GET'])
@app.route(f'/api/latest/<product>/<reach_id>', methods=['GET'])
@app.route(f'/api/v2/<product>/', methods=['GET'])
@app.route(f'/api/v2/<product>/<reach_id>', methods=['GET'])
@cross_origin()
def rest_endpoints_v2(product: str, reach_id: int = None):
product, reach_id, format, units, date, ensemble, start_date, end_date = handle_request(
request,
product,
reach_id,
)

log_request(version="v2",
product=product,
reach_id=request.args.get('reach_id', 0),
return_format=format,
source=request.args.get('source', 'other'), )

# forecast data products
if product == 'forecast':
return forecast(reach_id, date, units, format)
elif product in 'forecaststats':
return forecast_stats(reach_id, date, units, format)
elif product == 'forecastensembles':
return forecast_ensembles(reach_id, date, units, format, ensemble)
elif product == 'forecastrecords':
return forecast_records(reach_id, start_date, end_date, units, format)
elif product == 'forecastwarnings':
return forecast_warnings(date, format)
elif product == 'dates':
return forecast_dates(return_format=format)

# hindcast data products
elif product == 'retrospective':
return retrospective(reach_id, units, format)
elif product == 'returnperiods':
return return_periods(reach_id, units, format)
elif product == 'dailyaverages':
return daily_averages(reach_id, units, format)
elif product == 'monthlyaverages':
return monthly_averages(reach_id, units, format)
elif product == 'annualaverages':
return yearly_averages(reach_id, units, format)

# data availability
elif product == 'getreachid':
return get_reach_id(request, format=format)

elif product == "hydroviewer":
return hydroviewer(reach_id, start_date, date, units, format)

else:
return jsonify({'error': f'data product "{product}" not available'}), 201

Copy link

Choose a reason for hiding this comment

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

The route definitions are comprehensive and cover a wide range of data products. Verify that all routes are documented and that their parameters are validated to prevent injection attacks or unexpected errors.

+ # Add parameter validation for all routes

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
@app.route(f'/api/latest/<product>/', methods=['GET'])
@app.route(f'/api/latest/<product>/<reach_id>', methods=['GET'])
@app.route(f'/api/v2/<product>/', methods=['GET'])
@app.route(f'/api/v2/<product>/<reach_id>', methods=['GET'])
@cross_origin()
def rest_endpoints_v2(product: str, reach_id: int = None):
product, reach_id, format, units, date, ensemble, start_date, end_date = handle_request(
request,
product,
reach_id,
)
log_request(version="v2",
product=product,
reach_id=request.args.get('reach_id', 0),
return_format=format,
source=request.args.get('source', 'other'), )
# forecast data products
if product == 'forecast':
return forecast(reach_id, date, units, format)
elif product in 'forecaststats':
return forecast_stats(reach_id, date, units, format)
elif product == 'forecastensembles':
return forecast_ensembles(reach_id, date, units, format, ensemble)
elif product == 'forecastrecords':
return forecast_records(reach_id, start_date, end_date, units, format)
elif product == 'forecastwarnings':
return forecast_warnings(date, format)
elif product == 'dates':
return forecast_dates(return_format=format)
# hindcast data products
elif product == 'retrospective':
return retrospective(reach_id, units, format)
elif product == 'returnperiods':
return return_periods(reach_id, units, format)
elif product == 'dailyaverages':
return daily_averages(reach_id, units, format)
elif product == 'monthlyaverages':
return monthly_averages(reach_id, units, format)
elif product == 'annualaverages':
return yearly_averages(reach_id, units, format)
# data availability
elif product == 'getreachid':
return get_reach_id(request, format=format)
elif product == "hydroviewer":
return hydroviewer(reach_id, start_date, date, units, format)
else:
return jsonify({'error': f'data product "{product}" not available'}), 201
@app.route(f'/api/latest/<product>/', methods=['GET'])
@app.route(f'/api/latest/<product>/<reach_id>', methods=['GET'])
@app.route(f'/api/v2/<product>/', methods=['GET'])
@app.route(f'/api/v2/<product>/<reach_id>', methods=['GET'])
@cross_origin()
def rest_endpoints_v2(product: str, reach_id: int = None):
# Add parameter validation for all routes
product, reach_id, format, units, date, ensemble, start_date, end_date = handle_request(
request,
product,
reach_id,
)
log_request(version="v2",
product=product,
reach_id=request.args.get('reach_id', 0),
return_format=format,
source=request.args.get('source', 'other'), )
# forecast data products
if product == 'forecast':
return forecast(reach_id, date, units, format)
elif product in 'forecaststats':
return forecast_stats(reach_id, date, units, format)
elif product == 'forecastensembles':
return forecast_ensembles(reach_id, date, units, format, ensemble)
elif product == 'forecastrecords':
return forecast_records(reach_id, start_date, end_date, units, format)
elif product == 'forecastwarnings':
return forecast_warnings(date, format)
elif product == 'dates':
return forecast_dates(return_format=format)
# hindcast data products
elif product == 'retrospective':
return retrospective(reach_id, units, format)
elif product == 'returnperiods':
return return_periods(reach_id, units, format)
elif product == 'dailyaverages':
return daily_averages(reach_id, units, format)
elif product == 'monthlyaverages':
return monthly_averages(reach_id, units, format)
elif product == 'annualaverages':
return yearly_averages(reach_id, units, format)
# data availability
elif product == 'getreachid':
return get_reach_id(request, format=format)
elif product == "hydroviewer":
return hydroviewer(reach_id, start_date, date, units, format)
else:
return jsonify({'error': f'data product "{product}" not available'}), 201

@rileyhales rileyhales mentioned this pull request Mar 26, 2024
Closed
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Review Status

Actionable comments generated: 0

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between 00546b1 and f6a7923.
Files selected for processing (3)
  • app/v2/constants.py (1 hunks)
  • app/v2/controllers_historical.py (1 hunks)
  • app/v2/data.py (1 hunks)
Files skipped from review as they are similar to previous changes (3)
  • app/v2/constants.py
  • app/v2/controllers_historical.py
  • app/v2/data.py

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Review Status

Actionable comments generated: 0

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between f6a7923 and e32ab3d.
Files selected for processing (2)
  • app/v2/controllers_historical.py (1 hunks)
  • app/v2/response_formatters.py (1 hunks)
Files skipped from review as they are similar to previous changes (2)
  • app/v2/controllers_historical.py
  • app/v2/response_formatters.py

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Review Status

Actionable comments generated: 0

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between e32ab3d and 23cde35.
Files ignored due to path filters (1)
  • docs/swagger_doc.yaml is excluded by: !**/*.yaml
Files selected for processing (1)
  • README.md (1 hunks)
Additional comments: 2
README.md (2)
  • 1-1: The project name casing correction from "GEOGloWS" to "GEOGLOWS" is accurate and aligns with the PR objectives for standardization.
  • 3-3: While the static analysis tool flagged potential spelling mistakes and a missing article, these are largely false positives due to the technical nature of the content. However, it's always beneficial to have another set of eyes review technical documentation for clarity and correctness.

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.

None yet

2 participants