KSvend Claude Happy commited on
Commit ·
1b15a5f
1
Parent(s): 934a3a1
feat: add season_start/season_end to JobRequest with wrap support
Browse filesGenerated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
- app/models.py +13 -0
- tests/test_models.py +63 -0
app/models.py
CHANGED
|
@@ -93,6 +93,19 @@ class JobRequest(BaseModel):
|
|
| 93 |
time_range: TimeRange = Field(default_factory=TimeRange)
|
| 94 |
indicator_ids: list[str]
|
| 95 |
email: str
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 96 |
|
| 97 |
@field_validator("indicator_ids")
|
| 98 |
@classmethod
|
|
|
|
| 93 |
time_range: TimeRange = Field(default_factory=TimeRange)
|
| 94 |
indicator_ids: list[str]
|
| 95 |
email: str
|
| 96 |
+
season_start: int = Field(default=1, ge=1, le=12)
|
| 97 |
+
season_end: int = Field(default=12, ge=1, le=12)
|
| 98 |
+
|
| 99 |
+
def season_months(self) -> list[int]:
|
| 100 |
+
"""Return ordered list of month numbers in the analysis season.
|
| 101 |
+
|
| 102 |
+
Supports year-boundary wrapping: season_start=10, season_end=3
|
| 103 |
+
yields [10, 11, 12, 1, 2, 3].
|
| 104 |
+
"""
|
| 105 |
+
if self.season_start <= self.season_end:
|
| 106 |
+
return list(range(self.season_start, self.season_end + 1))
|
| 107 |
+
else:
|
| 108 |
+
return list(range(self.season_start, 13)) + list(range(1, self.season_end + 1))
|
| 109 |
|
| 110 |
@field_validator("indicator_ids")
|
| 111 |
@classmethod
|
tests/test_models.py
CHANGED
|
@@ -86,3 +86,66 @@ def test_indicator_result_fields():
|
|
| 86 |
)
|
| 87 |
assert result.status == "amber"
|
| 88 |
assert result.trend == "deteriorating"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 86 |
)
|
| 87 |
assert result.status == "amber"
|
| 88 |
assert result.trend == "deteriorating"
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
def test_job_request_season_defaults():
|
| 92 |
+
"""Default season is full year (1-12)."""
|
| 93 |
+
req = JobRequest(
|
| 94 |
+
aoi=AOI(name="Test", bbox=[36.75, -1.35, 36.95, -1.20]),
|
| 95 |
+
indicator_ids=["fires"],
|
| 96 |
+
email="t@t.com",
|
| 97 |
+
)
|
| 98 |
+
assert req.season_start == 1
|
| 99 |
+
assert req.season_end == 12
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
def test_job_request_season_months_normal():
|
| 103 |
+
"""Non-wrapping season: Apr-Sep."""
|
| 104 |
+
req = JobRequest(
|
| 105 |
+
aoi=AOI(name="Test", bbox=[36.75, -1.35, 36.95, -1.20]),
|
| 106 |
+
indicator_ids=["fires"],
|
| 107 |
+
email="t@t.com",
|
| 108 |
+
season_start=4,
|
| 109 |
+
season_end=9,
|
| 110 |
+
)
|
| 111 |
+
assert req.season_months() == [4, 5, 6, 7, 8, 9]
|
| 112 |
+
|
| 113 |
+
|
| 114 |
+
def test_job_request_season_months_wrapping():
|
| 115 |
+
"""Wrapping season: Oct-Mar (Southern Hemisphere)."""
|
| 116 |
+
req = JobRequest(
|
| 117 |
+
aoi=AOI(name="Test", bbox=[36.75, -1.35, 36.95, -1.20]),
|
| 118 |
+
indicator_ids=["fires"],
|
| 119 |
+
email="t@t.com",
|
| 120 |
+
season_start=10,
|
| 121 |
+
season_end=3,
|
| 122 |
+
)
|
| 123 |
+
assert req.season_months() == [10, 11, 12, 1, 2, 3]
|
| 124 |
+
|
| 125 |
+
|
| 126 |
+
def test_job_request_season_months_full_year():
|
| 127 |
+
"""Full year default."""
|
| 128 |
+
req = JobRequest(
|
| 129 |
+
aoi=AOI(name="Test", bbox=[36.75, -1.35, 36.95, -1.20]),
|
| 130 |
+
indicator_ids=["fires"],
|
| 131 |
+
email="t@t.com",
|
| 132 |
+
)
|
| 133 |
+
assert req.season_months() == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
|
| 134 |
+
|
| 135 |
+
|
| 136 |
+
def test_job_request_season_validation():
|
| 137 |
+
"""Season months must be 1-12."""
|
| 138 |
+
with pytest.raises(Exception):
|
| 139 |
+
JobRequest(
|
| 140 |
+
aoi=AOI(name="Test", bbox=[36.75, -1.35, 36.95, -1.20]),
|
| 141 |
+
indicator_ids=["fires"],
|
| 142 |
+
email="t@t.com",
|
| 143 |
+
season_start=0,
|
| 144 |
+
)
|
| 145 |
+
with pytest.raises(Exception):
|
| 146 |
+
JobRequest(
|
| 147 |
+
aoi=AOI(name="Test", bbox=[36.75, -1.35, 36.95, -1.20]),
|
| 148 |
+
indicator_ids=["fires"],
|
| 149 |
+
email="t@t.com",
|
| 150 |
+
season_end=13,
|
| 151 |
+
)
|