Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
9c65119
Create ratings_test.py
tlelv Sep 3, 2025
bdc4820
Merge pull request #193 from tlelv/ratings_CDA_test
tlelv Sep 3, 2025
3e00d69
Added JSONs and setup the file
tlelv Sep 4, 2025
bd4f643
Merge pull request #194 from tlelv/ratings_CDA_test
tlelv Sep 4, 2025
bf18b8e
Update ratings_CDA_test.py
tlelv Sep 6, 2025
a66acd7
Merge pull request #196 from tlelv/ratings_CDA_test
tlelv Sep 6, 2025
411cdc4
Update location.json
tlelv Sep 6, 2025
244f0bd
Merge pull request #197 from tlelv/ratings_CDA_test
tlelv Sep 6, 2025
38e6079
fix
tlelv Sep 6, 2025
d97382f
Merge pull request #198 from tlelv/ratings_CDA_test
tlelv Sep 6, 2025
6de7413
fix
tlelv Sep 6, 2025
e0f5065
Merge pull request #199 from tlelv/ratings_CDA_test
tlelv Sep 6, 2025
da1303a
added changes
tlelv Sep 10, 2025
8dbe22f
Merge pull request #201 from tlelv/ratings_CDA_test
tlelv Sep 10, 2025
7e64b51
update
tlelv Sep 10, 2025
014f359
Merge pull request #203 from tlelv/ratings_CDA_test
tlelv Sep 10, 2025
621d7e2
update
tlelv Sep 10, 2025
5b48816
Merge pull request #204 from tlelv/ratings_CDA_test
tlelv Sep 10, 2025
3215c57
Update location.json
tlelv Sep 10, 2025
f157ff6
Merge pull request #205 from tlelv/ratings_CDA_test
tlelv Sep 10, 2025
7a9f230
Switching to XML
tlelv Sep 11, 2025
f96e74c
Merge pull request #207 from tlelv/ratings_CDA_test
tlelv Sep 11, 2025
02ad2ac
Update ratings_CDA_test.py
tlelv Sep 16, 2025
220dfdb
Merge pull request #208 from tlelv/ratings_CDA_test
tlelv Sep 16, 2025
5a1bd95
Update ratings_CDA_test.py
tlelv Sep 16, 2025
98e33d6
Update ratings_CDA_test.py
tlelv Sep 16, 2025
3a668e6
Merge pull request #209 from tlelv/ratings_CDA_test
tlelv Sep 16, 2025
cfa8131
Updated compatability
tlelv Sep 18, 2025
b999d85
Merge pull request #211 from tlelv/ratings_CDA_test
tlelv Sep 18, 2025
d695d5c
Update ratings_CDA_test.py
tlelv Sep 18, 2025
b4a2ff4
Update ratings_CDA_test.py
tlelv Sep 18, 2025
b9ab24f
Merge pull request #212 from tlelv/ratings_CDA_test
tlelv Sep 18, 2025
1a28641
reset the tests
tlelv Sep 18, 2025
6f823e3
Merge pull request #213 from tlelv/ratings_CDA_test
tlelv Sep 18, 2025
ea6f9e8
Update ratings_CDA_test.py
tlelv Sep 18, 2025
4df570c
Merge pull request #214 from tlelv/ratings_CDA_test
tlelv Sep 18, 2025
a322d4e
Update ratings_CDA_test.py
tlelv Sep 23, 2025
64fb21b
Merge pull request #215 from tlelv/ratings_CDA_test
tlelv Sep 23, 2025
bdf0d51
Update ratings_CDA_test.py
tlelv Sep 23, 2025
d66d155
Merge pull request #216 from tlelv/ratings_CDA_test
tlelv Sep 23, 2025
f5166df
Update ratings_CDA_test.py
tlelv Oct 1, 2025
010bd9f
Merge pull request #221 from tlelv/ratings_CDA_test
tlelv Oct 1, 2025
84d8c77
Update ratings_CDA_test.py
tlelv Oct 1, 2025
039d183
Merge pull request #222 from tlelv/ratings_CDA_test
tlelv Oct 1, 2025
013b0a5
Update ratings_CDA_test.py
tlelv Oct 1, 2025
07d2ee7
Update ratings_CDA_test.py
tlelv Oct 1, 2025
dbe8e6b
Merge pull request #223 from tlelv/ratings_CDA_test
tlelv Oct 1, 2025
1acd449
Update ratings_CDA_test.py
tlelv Oct 15, 2025
bfc49fd
Update ratings_CDA_test.py
tlelv Oct 15, 2025
8243594
Made required changes
tlelv Oct 22, 2025
1c54a28
Update ratings_CDA_test.py
tlelv Oct 22, 2025
9c8c22a
format
tlelv Oct 22, 2025
07d456f
Update ratings_CDA_test.py
tlelv Oct 22, 2025
6c28544
Update ratings_CDA_test.py
tlelv Oct 22, 2025
a81b109
Update ratings_CDA_test.py
tlelv Oct 24, 2025
b302734
Update ratings_CDA_test.py
tlelv Oct 24, 2025
eaa7867
Update ratings_CDA_test.py
tlelv Oct 24, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
247 changes: 247 additions & 0 deletions tests/cda/ratings/ratings_CDA_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@
import json
import xml.etree.ElementTree as ET
from pathlib import Path

import pytest

import cwms
import cwms.ratings.ratings_spec as ratings_spec
import cwms.ratings.ratings_template as ratings_template
from cwms.api import ApiError


def get_xml_text(root, tag):
elem = root.find(tag)
if elem is None:
raise AssertionError(f"Missing <{tag}> in XML response")
return elem.text


RESOURCES = Path(__file__).parent.parent / "resources"

TEST_OFFICE = "MVP"
TEST_LOCATION_ID = "TestRating"

# Parse spec.xml
SPEC_XML = (RESOURCES / "spec.xml").read_text()
spec_root = ET.fromstring(SPEC_XML)

# Use direct assignment for test rating spec id
TEST_RATING_SPEC_ID = "TestRating.Stage;Flow.TEST.Spec-test"
TEST_TEMPLATE_ID2 = "Stage;Flow.TEST-2"
TEST_RATING_SPEC_ID2 = "TestRating.Stage;Flow.TEST-2.Spec-test"


@pytest.fixture(scope="module", autouse=True)
def setup_data():
location = json.load(open(RESOURCES / "location.json"))
cwms.store_location(location)
yield
try:
cwms.delete_location(TEST_LOCATION_ID, TEST_OFFICE, cascade_delete=True)
try:
ratings_spec.delete_rating_spec(
TEST_RATING_SPEC_ID, TEST_OFFICE, "DELETE_ALL"
)
except ApiError:
pass
try:
ratings_template.delete_rating_template(
"Stage;Flow.TEST", TEST_OFFICE, "DELETE_ALL"
)
except ApiError:
pass
try:
ratings_spec.delete_rating_spec(
TEST_RATING_SPEC_ID2, TEST_OFFICE, "DELETE_ALL"
)
except ApiError:
pass
try:
ratings_template.delete_rating_template(
TEST_TEMPLATE_ID2, TEST_OFFICE, "DELETE_ALL"
)
except ApiError:
pass
except ApiError:
pass


@pytest.fixture(autouse=True)
def init_session():
print("Initializing CWMS API session for ratings tests...")


def test_store_template():
TEMPLATE_XML = (RESOURCES / "template.xml").read_text()
TEST_TEMPLATE_ID = "Stage;Flow.TEST"

ratings_template.store_rating_template(TEMPLATE_XML)
fetched = ratings_template.get_rating_template(TEST_TEMPLATE_ID, TEST_OFFICE)
assert fetched.json["id"] == TEST_TEMPLATE_ID
assert fetched.json["office-id"] == TEST_OFFICE
assert TEST_TEMPLATE_ID in fetched.df["id"].values
assert TEST_OFFICE in fetched.df["office-id"].values
assert fetched.df["id"].iloc[0] == TEST_TEMPLATE_ID
assert fetched.df["office-id"].iloc[0] == TEST_OFFICE


def test_get_template():
TEST_TEMPLATE_ID = "Stage;Flow.TEST"

fetched = ratings_template.get_rating_template(TEST_TEMPLATE_ID, TEST_OFFICE)
assert fetched.json["id"] == TEST_TEMPLATE_ID
assert fetched.json["office-id"] == TEST_OFFICE
assert TEST_TEMPLATE_ID in fetched.df["id"].values
assert TEST_OFFICE in fetched.df["office-id"].values
assert fetched.df["id"].iloc[0] == TEST_TEMPLATE_ID
assert fetched.df["office-id"].iloc[0] == TEST_OFFICE


def test_get_rating_templates():
TEMPLATE_XML = (RESOURCES / "template.xml").read_text()
# Modify version to TEST-2
root = ET.fromstring(TEMPLATE_XML)
version = root.find("version")
if version is None:
version = ET.SubElement(root, "version")
version.text = "TEST-2"
updated_xml = ET.tostring(root, encoding="unicode", xml_declaration=True)

# Store new template and ensure cleanup
try:
ratings_template.store_rating_template(updated_xml)

# Fetch all templates
fetched = ratings_template.get_rating_templates(TEST_OFFICE)
df = fetched.df
assert "Stage;Flow.TEST" in df["id"].values
assert TEST_TEMPLATE_ID2 in df["id"].values
# Ensure at least two templates exist
ids = df["id"].values
assert len([i for i in ids if "Stage;Flow.TEST" in i]) >= 2
finally:
ratings_template.delete_rating_template(
TEST_TEMPLATE_ID2, TEST_OFFICE, "DELETE_ALL"
)


def test_update_template():
TEMPLATE_XML = (RESOURCES / "template.xml").read_text()
TEST_TEMPLATE_ID = "Stage;Flow.TEST"

root = ET.fromstring(TEMPLATE_XML)
desc = root.find("description")
if desc is None:
desc = ET.SubElement(root, "description")
desc.text = (desc.text or "") + " - updated"

updated_xml = ET.tostring(root, encoding="unicode", xml_declaration=True)
ratings_template.store_rating_template(updated_xml, fail_if_exists=False)
updated = ratings_template.get_rating_template(TEST_TEMPLATE_ID, TEST_OFFICE)
assert updated.json["description"].endswith(" - updated")
assert updated.df["description"].iloc[0].endswith(" - updated")


def test_store_rating_spec():
ratings_spec.store_rating_spec(SPEC_XML)
fetched = ratings_spec.get_rating_spec(TEST_RATING_SPEC_ID, TEST_OFFICE)
data_json = fetched.json
data_df = fetched.df
assert data_json["rating-id"] == TEST_RATING_SPEC_ID
assert data_json["office-id"] == TEST_OFFICE
assert TEST_RATING_SPEC_ID in data_df["rating-id"].values
assert TEST_OFFICE in data_df["office-id"].values
assert data_df["rating-id"].iloc[0] == TEST_RATING_SPEC_ID
assert data_df["office-id"].iloc[0] == TEST_OFFICE


def test_get_rating_spec():
fetched = ratings_spec.get_rating_spec(TEST_RATING_SPEC_ID, TEST_OFFICE)
data_json = fetched.json
data_df = fetched.df
assert data_json["rating-id"] == TEST_RATING_SPEC_ID
assert data_json["office-id"] == TEST_OFFICE
assert TEST_RATING_SPEC_ID in data_df["rating-id"].values
assert TEST_OFFICE in data_df["office-id"].values
assert data_df["rating-id"].iloc[0] == TEST_RATING_SPEC_ID
assert data_df["office-id"].iloc[0] == TEST_OFFICE


def test_get_rating_specs():
# Ensure second template is stored
TEMPLATE_XML2 = (RESOURCES / "template.xml").read_text()
root_template = ET.fromstring(TEMPLATE_XML2)
version = root_template.find("version")
if version is None:
version = ET.SubElement(root_template, "version")
version.text = "TEST-2"
template_xml_updated = ET.tostring(
root_template, encoding="unicode", xml_declaration=True
)

# Load spec XML
SPEC_XML2 = (RESOURCES / "spec.xml").read_text()
root = ET.fromstring(SPEC_XML2)
# Update rating-spec-id to use second template and version
rating_spec_id_elem = root.find("rating-spec-id")
if rating_spec_id_elem is None:
rating_spec_id_elem = ET.SubElement(root, "rating-spec-id")
rating_spec_id_elem.text = TEST_RATING_SPEC_ID2
template_elem = root.find("template-id")
if template_elem is None:
template_elem = ET.SubElement(root, "template-id")
template_elem.text = TEST_TEMPLATE_ID2
updated_xml = ET.tostring(root, encoding="unicode", xml_declaration=True)
# Store new rating spec and ensure cleanup
try:
ratings_template.store_rating_template(
template_xml_updated, fail_if_exists=False
)
ratings_spec.store_rating_spec(updated_xml, fail_if_exists=False)
# Fetch all rating specs
fetched = ratings_spec.get_rating_specs(TEST_OFFICE)
df = fetched.df
assert TEST_RATING_SPEC_ID in df["rating-id"].values
assert TEST_RATING_SPEC_ID2 in df["rating-id"].values
assert (
len(
[i for i in df["rating-id"].values if "TestRating.Stage;Flow.TEST" in i]
)
>= 2
)
finally:
ratings_spec.delete_rating_spec(TEST_RATING_SPEC_ID2, TEST_OFFICE, "DELETE_ALL")
ratings_template.delete_rating_template(
TEST_TEMPLATE_ID2, TEST_OFFICE, "DELETE_ALL"
)


def test_update_rating_spec():
desc = spec_root.find("description")
if desc is None:
desc = ET.SubElement(spec_root, "description")
desc.text = (desc.text or "") + " - updated"
updated_xml = ET.tostring(spec_root, encoding="unicode", xml_declaration=True)

ratings_spec.store_rating_spec(updated_xml, fail_if_exists=False)
fetched = ratings_spec.get_rating_spec(TEST_RATING_SPEC_ID, TEST_OFFICE)
data_json = fetched.json
data_df = fetched.df
assert data_json["description"].endswith(" - updated")
assert data_df["description"].iloc[0].endswith(" - updated")
assert data_df["description"].iloc[0].endswith(" - updated")


def test_delete_rating_spec():
ratings_spec.delete_rating_spec(TEST_RATING_SPEC_ID, TEST_OFFICE, "DELETE_ALL")
with pytest.raises(ApiError):
ratings_spec.get_rating_spec(TEST_RATING_SPEC_ID, TEST_OFFICE)


def test_delete_template():
TEST_TEMPLATE_ID = "Stage;Flow.TEST"

ratings_template.delete_rating_template(TEST_TEMPLATE_ID, TEST_OFFICE, "DELETE_ALL")
with pytest.raises(ApiError):
ratings_template.get_rating_template(TEST_TEMPLATE_ID, TEST_OFFICE)
15 changes: 15 additions & 0 deletions tests/cda/resources/location.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"name": "TestRating",
"latitude": 40.0,
"longitude": -105.0,
"elevation": 1000.0,
"horizontal-datum": "NAD83",
"vertical-datum": "NAVD88",
"office-id": "MVP",
"location-type": "TESTING",
"location-kind": "pytest_template_group",
"public-name": "Test Location",
"long-name": "Test Location for Rating Tests",
"timezone-name": "America/Chicago",
"nation": "US"
}
20 changes: 20 additions & 0 deletions tests/cda/resources/spec.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<rating-spec office-id="MVP">
<rating-spec-id>TestRating.Stage;Flow.TEST.Spec-test</rating-spec-id>
<template-id>Stage;Flow.TEST</template-id>
<location-id>TestRating</location-id>
<version>Spec-test</version>
<source-agency>USGS</source-agency>
<in-range-method>LINEAR</in-range-method>
<out-range-low-method>NEAREST</out-range-low-method>
<out-range-high-method>NEAREST</out-range-high-method>
<active>true</active>
<auto-update>true</auto-update>
<auto-activate>true</auto-activate>
<auto-migrate-extension>true</auto-migrate-extension>
<ind-rounding-specs>
<ind-rounding-spec position="1">2223456782</ind-rounding-spec>
</ind-rounding-specs>
<dep-rounding-spec>2222233332</dep-rounding-spec>
<description>TESTing rating spec</description>
</rating-spec>
15 changes: 15 additions & 0 deletions tests/cda/resources/template.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<rating-template office-id="MVP">
<parameters-id>Stage;Flow</parameters-id>
<version>TEST</version>\
<ind-parameter-specs>
<ind-parameter-spec position="1">
<parameter>Stage</parameter>
<in-range-method>LINEAR</in-range-method>
<out-range-low-method>LINEAR</out-range-low-method>
<out-range-high-method>LINEAR</out-range-high-method>
</ind-parameter-spec>
</ind-parameter-specs>
<dep-parameter>Flow</dep-parameter>
<description>Expanded, Shift-Adjusted Stream Rating</description>
</rating-template>