Rename the middle rain tiers and merge Mod–Heavy into Heavy (#214)

The rain-intensity scale drops its two hyphenated compound labels for single
words and loses a tier, going from eight to seven:

  Trace / Light / Brisk / Typical / Heavy / Very Heavy / Extreme

- Light–Mod -> Brisk, Moderate -> Typical (renames only; classes unchanged).
- Mod–Heavy is merged up into Heavy, whose floor drops from the 75th to the 60th
  rain-day percentile, so Heavy now spans 60–90.
- The lightest tier (already the merged Very Light) is renamed Trace.

grading.py RAIN_BANDS and the frontend SCALE_RAIN mirror stay in lockstep, and
the detail-view ladder derives from the table so it follows automatically. The
now-unreferenced wet-6 colour token is kept so any day still cached under that
class renders until the derived store recomputes; the chart's percentile fan also
keeps it for a smooth gradient.

Claude-Session: https://claude.ai/code/session_013dRZmX9D3JEntfMKWMTWZ8
This commit is contained in:
Emi Griffith 2026-07-19 21:38:30 -07:00 committed by GitHub
parent c9abad3e13
commit 581bdaca04
2 changed files with 22 additions and 18 deletions

View file

@ -47,19 +47,17 @@ TEMP_BANDS = [
# Precipitation is graded ONLY among days that actually rained (>= RAIN_THRESHOLD) # Precipitation is graded ONLY among days that actually rained (>= RAIN_THRESHOLD)
# in the seasonal window — a "rain percentile". Rain is one-directional (heavier = # in the seasonal window — a "rain percentile". Rain is one-directional (heavier =
# more extreme), so these 8 tiers are sequential light->heavy, using the SAME cut # more extreme), so these 7 tiers are sequential light->heavy, using the SAME cut
# points as temperature. Dry days are handled separately (the "dry" class, colored # points as temperature. Dry days are handled separately (the "dry" class, colored
# by dry streak in the UI). Same [lower, upper) convention as TEMP_BANDS. # by dry streak in the UI). Same [lower, upper) convention as TEMP_BANDS.
RAIN_BANDS = [ RAIN_BANDS = [
(99, "Extreme", "wet-9"), # heaviest rain for the season (darkest) (99, "Extreme", "wet-9"), # heaviest rain for the season (darkest)
(90, "Very Heavy", "wet-8"), # 90-99 (90, "Very Heavy", "wet-8"), # 90-99
(75, "Heavy", "wet-7"), # 75-90 (60, "Heavy", "wet-7"), # 60-90 (the old ModHeavy tier merged in)
(60, "ModHeavy", "wet-6"), # 60-75 (40, "Typical", "wet-5"), # 40-60 (was Moderate)
(40, "Moderate", "wet-5"), # 40-60 (25, "Brisk", "wet-4"), # 25-40 (was LightMod)
(25, "LightMod", "wet-4"), # 25-40
(10, "Light", "wet-3"), # 10-25 (10, "Light", "wet-3"), # 10-25
(0, "Very Light", "wet-2"), # <10 the lightest measurable rain (was two (0, "Trace", "wet-2"), # <10 the lightest measurable rain
# tiers, Very Light + Trace, now merged)
] ]

View file

@ -69,11 +69,11 @@ def test_dry_day_gets_dry_class_without_percentile():
def test_rain_percentile_ranks_among_rain_days_only(): def test_rain_percentile_ranks_among_rain_days_only():
# 7 dry days + rain days [0.1, 0.2, 0.4]; 0.2 ranks among the 3 rain days: # 7 dry days + rain days [0.1, 0.2, 0.4]; 0.2 ranks among the 3 rain days:
# less=1, equal=1 -> (1 + 0.5) / 3 = 50% -> Moderate, unaffected by the dry mass. # less=1, equal=1 -> (1 + 0.5) / 3 = 50% -> Typical, unaffected by the dry mass.
samples = np.array([0.0] * 7 + [0.1, 0.2, 0.4]) samples = np.array([0.0] * 7 + [0.1, 0.2, 0.4])
g = grading._grade_precip(samples, 0.2) g = grading._grade_precip(samples, 0.2)
assert g["percentile"] == 50.0 assert g["percentile"] == 50.0
assert g["grade"] == "Moderate" assert g["grade"] == "Typical"
def test_rain_with_no_historical_rain_days_is_extreme(): def test_rain_with_no_historical_rain_days_is_extreme():
@ -81,16 +81,22 @@ def test_rain_with_no_historical_rain_days_is_extreme():
assert g["percentile"] == 100.0 and g["class"] == "wet-9" assert g["percentile"] == 100.0 and g["class"] == "wet-9"
def test_lightest_rain_day_grades_very_light_not_trace(): def test_rain_scale_labels_and_merges():
# The former "Trace" tier (<1st percentile of rain days) was merged into # The seven-tier scale: Trace / Light / Brisk / Typical / Heavy / Very Heavy /
# "Very Light", which now bottoms out the scale at 0. # Extreme. LightMod and Moderate were renamed; ModHeavy was merged up into
assert "Trace" not in [b[1] for b in grading.RAIN_BANDS] # Heavy (which now floors at the 60th percentile).
assert grading.RAIN_BANDS[-1] == (0, "Very Light", "wet-2") labels = [b[1] for b in grading.RAIN_BANDS]
rain = np.arange(1, 201, dtype=float) / 100.0 # 200 rain days: 0.01 .. 2.00 assert labels == ["Extreme", "Very Heavy", "Heavy", "Typical", "Brisk", "Light", "Trace"]
for gone in ("Very Light", "LightMod", "Moderate", "ModHeavy"):
assert gone not in labels
# A rain day at the old ModHeavy range (6075 pct) is now Heavy.
rain = np.arange(1, 201, dtype=float) / 100.0
samples = np.concatenate([np.zeros(20), rain]) samples = np.concatenate([np.zeros(20), rain])
g = grading._grade_precip(samples, 0.01) # the very lightest -> <1 pct g = grading._grade_precip(samples, 1.35) # ~67th percentile of rain days
assert g["percentile"] < 1 assert 60 <= g["percentile"] < 75 and g["grade"] == "Heavy" and g["class"] == "wet-7"
assert g["grade"] == "Very Light" and g["class"] == "wet-2" # The lightest measurable rain is Trace, bottoming the scale at 0.
g0 = grading._grade_precip(samples, 0.01)
assert g0["grade"] == "Trace" and g0["class"] == "wet-2"
# ---- dry streaks --------------------------------------------------------------- # ---- dry streaks ---------------------------------------------------------------