A Python client library for interacting with Hilltop Server APIs, developed by Horizons Regional Council as a dependency of Hydrobot. WHURL provides a clean, Pythonic interface for fetching environmental and scientific data from Hilltop servers.
WHURL (Which Hydro URL) is designed to simplify interactions with Hilltop Server, a platform commonly used for storing and managing environmental data such as water levels, flow rates, rainfall measurements, and other scientific observations. The library handles URL generation, request validation, XML parsing, and provides structured Python objects for easy data manipulation.
⚠️ Work in Progress: This library is currently under active development. While core functionality is stable and tested, not all Hilltop API endpoints are supported yet. See the Planned Features section for upcoming enhancements.
- Simple Client Interface: Easy-to-use
HilltopClientwith methods for all major Hilltop operations - Request Validation: Pydantic-based validation ensures proper request formatting
- Type Safety: Full type hints for better IDE support and code reliability
- XML Response Parsing: Automatic parsing of Hilltop XML responses into Python objects
- Error Handling: Comprehensive exception handling with detailed error messages
- Configuration Management: Support for environment variables and direct configuration
- Context Manager Support: Proper resource cleanup with
withstatement support
- Python 3.11 or higher
- Poetry for dependency management
- Internet connectivity to reach your Hilltop server
pip install whurlfrom whurl.client import HilltopClient
# Option 1: Direct configuration
client = HilltopClient(
base_url="https://your-hilltop-server.com",
hts_endpoint="data.hts",
timeout=60 # Optional, defaults to 60 seconds
)
# Option 2: Using environment variables
# Set HILLTOP_BASE_URL and HILLTOP_HTS_ENDPOINT
client = HilltopClient()
# Always use as a context manager for proper cleanup
with client:
# Your code here
status = client.get_status()
print(f"Server status: {status}")WHURL supports configuration via environment variables:
export HILLTOP_BASE_URL="https://your-hilltop-server.com"
export HILLTOP_HTS_ENDPOINT="data.hts"Or create a .env file:
HILLTOP_BASE_URL=https://your-hilltop-server.com
HILLTOP_HTS_ENDPOINT=data.htsfrom whurl.client import HilltopClient
with HilltopClient() as client:
# Get server status
status = client.get_status()
print(f"Server is running: {status.server}")
# List all sites
sites = client.get_site_list()
print(f"Found {len(sites.sites)} sites")
# Get measurements for a specific site
measurements = client.get_measurement_list(site="YourSiteName")
for measurement in measurements.measurements:
print(f"Measurement: {measurement.name}, Units: {measurement.units}")The main client class for interacting with Hilltop servers.
HilltopClient(
base_url: str | None = None,
hts_endpoint: str | None = None,
timeout: int = 60
)Parameters:
base_url: The base URL of your Hilltop server (e.g., "https://data.council.govt.nz")hts_endpoint: The HTS endpoint path (e.g., "foo.hts")timeout: Request timeout in seconds (default: 60)
Get the status of the Hilltop server.
status = client.get_status()
print(status.server) # Server information
print(status.version) # Hilltop versionReturns: StatusResponse object
Retrieve a list of sites from the Hilltop server.
# Get all sites
sites = client.get_site_list()
# Get sites with location information
sites = client.get_site_list(location="Yes")
# Filter by measurement type
sites = client.get_site_list(measurement="Flow")
# Filter by collection
sites = client.get_site_list(collection="River")Parameters:
location: "Yes", "LatLong", or None - Include location datameasurement: Filter sites by measurement typecollection: Filter sites by collection namesite_parameters: Include site parametersbounding_box: Spatial filter (format: "x1,y1,x2,y2")
Returns: SiteListResponse object
Get available measurements for a site or collection.
# Get measurements for a specific site
measurements = client.get_measurement_list(site="YourSiteName")
# Get measurements with units information
measurements = client.get_measurement_list(site="YourSiteName", units="Yes")
# Get measurements for a collection
measurements = client.get_measurement_list(collection="River")Parameters:
site: Site name (required if collection not specified)collection: Collection nameunits: "Yes" to include units information
Returns: MeasurementListResponse object
Get detailed information about a specific site.
site_info = client.get_site_info(site="YourSiteName")
print(site_info.site_name)
print(site_info.location)Parameters:
site: Site name (required)
Returns: SiteInfoResponse object
Retrieve measurement data from the Hilltop server.
# Get recent data
data = client.get_data(
site="YourSiteName",
measurement="Flow",
from_datetime="2024-01-01T00:00:00",
to_datetime="2024-01-31T23:59:59"
)
# Get data with statistics
data = client.get_data(
site="YourSiteName",
measurement="Flow",
method="Average",
interval="1 day"
)
# Get data with custom time intervals
data = client.get_data(
site="YourSiteName",
measurement="Rainfall",
time_interval="1 hour",
alignment="00:00"
)Parameters:
site: Site namemeasurement: Measurement namefrom_datetime: Start datetime (ISO format)to_datetime: End datetime (ISO format)method: Statistical method ("Interpolate", "Average", "Total", "Moving Average", "EP", "Extrema")interval: Time interval for statistics (e.g., "1 day", "4 hours")time_interval: Regular time interval for dataalignment: Time alignment (e.g., "00:00")collection: Collection namegap_tolerance: Maximum gap between data pointsformat: Output format ("Native" or other formats)
Returns: GetDataResponse object
Get the available time range for a measurement at a site.
time_range = client.get_time_range(
site="YourSiteName",
measurement="Flow"
)
print(f"Data available from {time_range.from_time} to {time_range.to_time}")Parameters:
site: Site name (required)measurement: Measurement name (required)format: "json" for JSON response, omit for XML
Returns: TimeRangeResponse object
Get a list of available collections.
collections = client.get_collection_list()
for collection in collections.collections:
print(f"Collection: {collection.name}")Returns: CollectionListResponse object
from whurl.client import HilltopClient
import pandas as pd
with HilltopClient() as client:
# Monitor water levels at multiple sites
sites = ["Site1", "Site2", "Site3"]
water_levels = {}
for site in sites:
data = client.get_data(
site=site,
measurement="Water Level",
from_datetime="2024-01-01T00:00:00",
to_datetime="2024-01-31T23:59:59"
)
water_levels[site] = data.to_dataframe() # Convert to pandas DataFrame
# Analyze the data
for site, df in water_levels.items():
print(f"{site}: Max level = {df['Value'].max():.2f} m")with HilltopClient() as client:
# Get hourly rainfall data
rainfall = client.get_data(
site="RainfallSite",
measurement="Rainfall",
method="Total",
interval="1 hour",
from_datetime="2024-01-01T00:00:00",
to_datetime="2024-01-07T23:59:59"
)
# Calculate daily totals
daily_rain = client.get_data(
site="RainfallSite",
measurement="Rainfall",
method="Total",
interval="1 day",
from_datetime="2024-01-01T00:00:00",
to_datetime="2024-01-31T23:59:59"
)with HilltopClient() as client:
# Discover sites with flow measurements
sites = client.get_site_list(measurement="Flow", location="LatLong")
for site in sites.sites:
print(f"Site: {site.name}")
print(f"Location: {site.latitude}, {site.longitude}")
# Get available measurements
measurements = client.get_measurement_list(site=site.name, units="Yes")
for measurement in measurements.measurements:
print(f" - {measurement.name} ({measurement.units})")
# Get data time range
time_range = client.get_time_range(site=site.name, measurement="Flow")
print(f" Data: {time_range.from_time} to {time_range.to_time}")
print()WHURL provides comprehensive error handling through custom exceptions:
from whurl.client import HilltopClient
from whurl.exceptions import (
HilltopError,
HilltopConfigError,
HilltopRequestError,
HilltopResponseError,
HilltopParseError
)
try:
with HilltopClient() as client:
data = client.get_data(site="InvalidSite", measurement="Flow")
except HilltopConfigError as e:
print(f"Configuration error: {e}")
# Handle missing base_url or hts_endpoint
except HilltopRequestError as e:
print(f"Request error: {e}")
# Handle invalid parameters
except HilltopResponseError as e:
print(f"Server error: {e}")
# Handle HTTP errors or server-side issues
except HilltopParseError as e:
print(f"Parse error: {e}")
# Handle XML parsing errors
except HilltopError as e:
print(f"General Hilltop error: {e}")
# Handle any other Hilltop-related errorsHilltopError- Base exception for all WHURL errorsHilltopConfigError- Configuration issues (missing URLs, credentials)HilltopRequestError- Request validation errors (invalid parameters)HilltopResponseError- HTTP and server response errorsHilltopParseError- XML parsing and data conversion errors
WHURL uses Pydantic for request validation and type safety. Common validation rules:
# Valid time interval formats
"10 seconds" # or "10 second", "10s"
"5 minutes" # or "5 minute", "5m"
"1 hour" # or "1h"
"1 day" # or "1d"
"1 week" # or "1w"
"1 month" # or "1mo"
"1 year" # or "1y"# ISO 8601 format required
"2024-01-01T00:00:00"
"2024-12-31T23:59:59""Interpolate"- Interpolated values"Average"- Average over interval"Total"- Sum over interval"Moving Average"- Moving average"EP"- End period"Extrema"- Min/max values
WHURL uses Poetry for dependency management and packaging. This ensures reproducible builds and simplifies development workflows.
# Clone the repository
git clone https://github.com/HorizonsRC/whurl.git
cd whurl
# Install Poetry if you haven't already
pip install poetry
# Install all dependencies (runtime + development)
poetry install
# Activate the virtual environment
poetry shell
# Run tests to verify setup
poetry run pytestIf you prefer using pip directly:
# Clone the repository
git clone https://github.com/HorizonsRC/whurl.git
cd whurl
# Install in development mode
pip install -e .
# Install development dependencies
pip install pytest pytest-mock pytest-httpx
# Run tests
python -m pytest tests/WHURL uses a comprehensive testing strategy that includes both local mocked tests and remote API validation using a fixture cache system. For detailed information about testing categories, modes, and troubleshooting, see the Testing Strategy Documentation.
This project uses:
- Type hints for better code documentation
- Pydantic for data validation
- Black for code formatting (if applicable)
- PEP 257 compliant docstrings
| Variable | Description | Example |
|---|---|---|
HILLTOP_BASE_URL |
Base URL of Hilltop server | https://data.council.govt.nz |
HILLTOP_HTS_ENDPOINT |
HTS endpoint file | foo.hts |
client = HilltopClient(
base_url="https://your-server.com", # Taken from .env file if not set.
hts_endpoint="data.hts", # Taken from .env file if not set.
timeout=60 # Optional, default 60 seconds
)This project is licensed under the GNU General Public License v3.0 - see the LICENSE file for details.
- Author: Nic Mostert
- Email: nicolas.mostert@horizons.govt.nz
- Organization: Horizons Regional Council
- Repository: https://github.com/HorizonsRC/whurl
Developed by Horizons Regional Council for environmental data management and analysis. Special thanks to the Hilltop development team for their comprehensive API documentation.
Version: 0.1.1
Python Compatibility: 3.11+
License: GPL-3.0