simplify test_last_full_moon.py: delegate to run_phase() with no_upload

Removes the bypass of east-frame verification — the script now runs the
complete production pipeline (dark-window filtering, moon detection on
east frames, NASA Dial-a-Moon fetch, render_phase_closeup) by calling
mpm.run_phase() with no_upload=True and a fixed test output path.

Retains the explicit sun_events_in_range() call as a regression check
for the bare-topos fix before the main pipeline runs.

https://claude.ai/code/session_01JieSeNQZ3X6fhsb11YyJrK
This commit is contained in:
Claude
2026-05-02 12:53:14 +00:00
parent d8bf01f86f
commit a470879d1e
+22 -64
View File
@@ -1,15 +1,14 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
"""test_last_full_moon.py — produce a full-moon close-up for the most recent full moon. """test_last_full_moon.py — run the full moon pipeline for the most recent full moon.
Runs the same NASA Dial-a-Moon fetch + render_phase_closeup pipeline that Identical to `moon-phase-monthly.sh --phase full --no-upload` except the
moon-phase-monthly.sh uses, but bypasses the east-frame witness step so it output is written to test_last_full_moon_out.jpg in the sky-cam directory
works any time without needing captured frames. instead of the normal movies tree.
Also exercises sun_events_in_range() (the sunrise/sunset almanac call that Includes east-frame detection, dark-window filtering, NASA Dial-a-Moon
requires a bare topos, not an earth+topos observer) as a regression check fetch, and render_phase_closeup — the complete production path.
for that fix. Also calls sun_events_in_range() explicitly as a regression check for
the sunrise_sunset() bare-topos fix.
Output: test_last_full_moon_out.jpg in the sky-cam directory.
Run from the sky-cam directory: Run from the sky-cam directory:
@@ -28,73 +27,32 @@ OUT_PATH = HERE / 'test_last_full_moon_out.jpg'
def main() -> int: def main() -> int:
import moon_phase as mp import moon_phase as mp
import moon_dialamoon import moon_phase_monthly as mpm
from moon_composite import render_phase_closeup
# ── Regression check: sun_events_in_range() (bare-topos fix) ─────────────
now = datetime.now(timezone.utc) now = datetime.now(timezone.utc)
# ── Find the most recent full moon ────────────────────────────────────────
candidates = mp.full_moons_in_range(now - timedelta(days=35), now) candidates = mp.full_moons_in_range(now - timedelta(days=35), now)
if not candidates: if not candidates:
print("ERROR: no full moon found in the last 35 days", file=sys.stderr) print("ERROR: no full moon found in the last 35 days", file=sys.stderr)
return 1 return 1
fm = candidates[-1] fm = candidates[-1]
illum = mp.illumination(fm)
illum_pct = round(illum * 100)
pa = mp.phase_angle(fm)
alt, az = mp.altaz(fm)
para = mp.parallactic_angle(fm)
local_str = mp._format_local(fm)
print(f"last full moon (UTC) : {fm.strftime('%Y-%m-%dT%H:%M:%SZ')}")
print(f"local : {local_str}")
print(f"illumination : {illum:.4f} ({illum_pct}%)")
print(f"phase angle : {pa:.2f}° (0° = full)")
print(f"altitude / azimuth : {alt:.2f}° / {az:.2f}°")
print(f"parallactic angle : {para:.2f}°")
print()
# ── Sunrise/sunset for that day (regression check for topos fix) ──────────
day_start = fm.replace(hour=0, minute=0, second=0, microsecond=0) day_start = fm.replace(hour=0, minute=0, second=0, microsecond=0)
day_end = day_start + timedelta(days=1) events = mp.sun_events_in_range(day_start, day_start + timedelta(days=1))
events = mp.sun_events_in_range(day_start, day_end) print(f"sun events on {day_start.strftime('%Y-%m-%d')} UTC (topos fix check):")
if events: for t, is_rise in events:
print(f"sun events on {day_start.strftime('%Y-%m-%d')} UTC:") print(f" {'sunrise' if is_rise else 'sunset '} {t.strftime('%H:%M:%S')} UTC")
for t, is_rise in events:
label = "sunrise" if is_rise else "sunset "
print(f" {label} {t.strftime('%H:%M:%S')} UTC ({mp._format_local(t)})")
else:
print(f"no sun events on {day_start.strftime('%Y-%m-%d')} (polar location?)")
print() print()
# ── Fetch NASA Dial-a-Moon render for that hour ─────────────────────────── # ── Full production pipeline ──────────────────────────────────────────────
print(f"fetching NASA Dial-a-Moon for {fm.strftime('%Y-%m-%dT%HZ')} ...") return mpm.run_phase(
try: phase='full',
nasa_path = moon_dialamoon.fetch_for_time(fm) target_utc=fm,
except Exception as e: cam=mpm.SUNRISE_CAM,
print(f"ERROR: dial-a-moon fetch failed: {e}", file=sys.stderr) dry_run=False,
return 2 no_upload=True,
print(f"dial-a-moon cache : {nasa_path}") out_path=str(OUT_PATH),
print()
# ── Render full-screen composite ──────────────────────────────────────────
caption = (
f"Full Moon — {illum_pct}% lit — {fm.strftime('%B %Y')}"
f"{local_str} — NASA SVS Dial-a-Moon"
) )
render_phase_closeup(
str(nasa_path),
str(OUT_PATH),
output_size=(1920, 1080),
moon_height_pct=0.92,
caption=caption,
when_utc=fm,
)
print(f"wrote {OUT_PATH}")
print()
print("OK")
return 0
if __name__ == "__main__": if __name__ == "__main__":