astroapi-io-client 0.2.3__tar.gz → 0.2.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. {astroapi_io_client-0.2.3 → astroapi_io_client-0.2.4}/PKG-INFO +4 -3
  2. {astroapi_io_client-0.2.3 → astroapi_io_client-0.2.4}/README.md +3 -2
  3. {astroapi_io_client-0.2.3 → astroapi_io_client-0.2.4}/pyproject.toml +1 -1
  4. {astroapi_io_client-0.2.3 → astroapi_io_client-0.2.4}/src/astroapi_client/__init__.py +3 -3
  5. {astroapi_io_client-0.2.3 → astroapi_io_client-0.2.4}/src/astroapi_client/_validation.py +26 -0
  6. {astroapi_io_client-0.2.3 → astroapi_io_client-0.2.4}/src/astroapi_client/client.py +7 -3
  7. {astroapi_io_client-0.2.3 → astroapi_io_client-0.2.4}/src/astroapi_client/types.py +27 -0
  8. {astroapi_io_client-0.2.3 → astroapi_io_client-0.2.4}/src/astroapi_io_client.egg-info/PKG-INFO +4 -3
  9. {astroapi_io_client-0.2.3 → astroapi_io_client-0.2.4}/tests/test_client.py +6 -0
  10. {astroapi_io_client-0.2.3 → astroapi_io_client-0.2.4}/LICENSE.txt +0 -0
  11. {astroapi_io_client-0.2.3 → astroapi_io_client-0.2.4}/setup.cfg +0 -0
  12. {astroapi_io_client-0.2.3 → astroapi_io_client-0.2.4}/src/astroapi_client/errors.py +0 -0
  13. {astroapi_io_client-0.2.3 → astroapi_io_client-0.2.4}/src/astroapi_client/py.typed +0 -0
  14. {astroapi_io_client-0.2.3 → astroapi_io_client-0.2.4}/src/astroapi_client/receipts.py +0 -0
  15. {astroapi_io_client-0.2.3 → astroapi_io_client-0.2.4}/src/astroapi_io_client.egg-info/SOURCES.txt +0 -0
  16. {astroapi_io_client-0.2.3 → astroapi_io_client-0.2.4}/src/astroapi_io_client.egg-info/dependency_links.txt +0 -0
  17. {astroapi_io_client-0.2.3 → astroapi_io_client-0.2.4}/src/astroapi_io_client.egg-info/requires.txt +0 -0
  18. {astroapi_io_client-0.2.3 → astroapi_io_client-0.2.4}/src/astroapi_io_client.egg-info/top_level.txt +0 -0
  19. {astroapi_io_client-0.2.3 → astroapi_io_client-0.2.4}/tests/test_aspect_windows_client.py +0 -0
  20. {astroapi_io_client-0.2.3 → astroapi_io_client-0.2.4}/tests/test_calendar_export.py +0 -0
  21. {astroapi_io_client-0.2.3 → astroapi_io_client-0.2.4}/tests/test_davison_input.py +0 -0
  22. {astroapi_io_client-0.2.3 → astroapi_io_client-0.2.4}/tests/test_house_ingresses_client.py +0 -0
  23. {astroapi_io_client-0.2.3 → astroapi_io_client-0.2.4}/tests/test_next_expansions_client.py +0 -0
  24. {astroapi_io_client-0.2.3 → astroapi_io_client-0.2.4}/tests/test_onboarding_context_client.py +0 -0
  25. {astroapi_io_client-0.2.3 → astroapi_io_client-0.2.4}/tests/test_planetary_events_client.py +0 -0
  26. {astroapi_io_client-0.2.3 → astroapi_io_client-0.2.4}/tests/test_varga_input.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: astroapi-io-client
3
- Version: 0.2.3
3
+ Version: 0.2.4
4
4
  Summary: Synchronous Python client for the AstroAPI.io calculation gateway
5
5
  License-Expression: ISC
6
6
  Classifier: Programming Language :: Python :: 3
@@ -14,11 +14,11 @@ Dynamic: license-file
14
14
 
15
15
  # AstroAPI.io Python client
16
16
 
17
- The R196 source version `astroapi-io-client==0.2.3` covers all forty-four calculation routes: forty-three standard operations plus the separate Pro Nakshatra Timeline. Minor-body calculations are not part of the contract.
17
+ The R197 source version `astroapi-io-client==0.2.4` covers all forty-five calculation routes: forty-four standard operations plus the separate Pro Nakshatra Timeline. Minor-body calculations are not part of the contract.
18
18
 
19
19
  A synchronous client for Python 3.10+, with no runtime dependencies.
20
20
  The package import is `astroapi_client`; the distribution name is
21
- `astroapi-io-client`. Version 0.2.2 is the published R195 package; 0.2.3 is source-only and package availability never implies deployed API capabilities.
21
+ `astroapi-io-client`. Version 0.2.2 is the published R195 package; 0.2.4 is source-only and package availability never implies deployed API capabilities.
22
22
 
23
23
  Install from PyPI with `python -m pip install astroapi-io-client==0.2.2`, or from a local checkout using `python -m pip install ./clients/python`.
24
24
  Building needs setuptools and wheel; the installed client uses only the standard
@@ -97,6 +97,7 @@ Type hints describe the server contract and do not perform response validation.
97
97
  | `convention_compare` **beta** | `calculateConventionCompare` | two profiles with one birth, or two trusted signed responses | field-level differences without ranking or interpretation |
98
98
  | `chart_robustness` **beta** | `calculateChartRobustness` | unknown or approximate birth time plus 2–3 convention profiles | sampled stability classifications without rectification, ranking, or prediction |
99
99
  | `civil_time_audit` **beta** | `calculateCivilTimeAudit` | pinned place, local date/time, one explicit profile | both repeated-time chart candidates and exact 22-fact differences, or no shifted gap time |
100
+ | `chart_input_readiness` **beta** | `calculateChartInputReadiness` | pinned place, exact/approximate/unknown time quality, explicit profile | route readiness plus resolved, candidate, or sampled evidence for 22 facts |
100
101
  | `synastry_svg` **beta** | `calculateSynastrySvg` | two births; optional zodiac/houses/theme/aspects/`include_png` | `result["svg"]`, optional `result["png"]` |
101
102
  | `transit_svg` **beta** | `calculateTransitSvg` | birth and transit date; optional display controls/`include_png` | `result["svg"]`, optional `result["png"]` |
102
103
  | `natal_report` **beta** | `calculateNatalReport` | birth; optional zodiac/houses/theme/SVG/download | `result["html"]`, source-linked `result["facts"]`, optional `result["download"]` |
@@ -1,10 +1,10 @@
1
1
  # AstroAPI.io Python client
2
2
 
3
- The R196 source version `astroapi-io-client==0.2.3` covers all forty-four calculation routes: forty-three standard operations plus the separate Pro Nakshatra Timeline. Minor-body calculations are not part of the contract.
3
+ The R197 source version `astroapi-io-client==0.2.4` covers all forty-five calculation routes: forty-four standard operations plus the separate Pro Nakshatra Timeline. Minor-body calculations are not part of the contract.
4
4
 
5
5
  A synchronous client for Python 3.10+, with no runtime dependencies.
6
6
  The package import is `astroapi_client`; the distribution name is
7
- `astroapi-io-client`. Version 0.2.2 is the published R195 package; 0.2.3 is source-only and package availability never implies deployed API capabilities.
7
+ `astroapi-io-client`. Version 0.2.2 is the published R195 package; 0.2.4 is source-only and package availability never implies deployed API capabilities.
8
8
 
9
9
  Install from PyPI with `python -m pip install astroapi-io-client==0.2.2`, or from a local checkout using `python -m pip install ./clients/python`.
10
10
  Building needs setuptools and wheel; the installed client uses only the standard
@@ -83,6 +83,7 @@ Type hints describe the server contract and do not perform response validation.
83
83
  | `convention_compare` **beta** | `calculateConventionCompare` | two profiles with one birth, or two trusted signed responses | field-level differences without ranking or interpretation |
84
84
  | `chart_robustness` **beta** | `calculateChartRobustness` | unknown or approximate birth time plus 2–3 convention profiles | sampled stability classifications without rectification, ranking, or prediction |
85
85
  | `civil_time_audit` **beta** | `calculateCivilTimeAudit` | pinned place, local date/time, one explicit profile | both repeated-time chart candidates and exact 22-fact differences, or no shifted gap time |
86
+ | `chart_input_readiness` **beta** | `calculateChartInputReadiness` | pinned place, exact/approximate/unknown time quality, explicit profile | route readiness plus resolved, candidate, or sampled evidence for 22 facts |
86
87
  | `synastry_svg` **beta** | `calculateSynastrySvg` | two births; optional zodiac/houses/theme/aspects/`include_png` | `result["svg"]`, optional `result["png"]` |
87
88
  | `transit_svg` **beta** | `calculateTransitSvg` | birth and transit date; optional display controls/`include_png` | `result["svg"]`, optional `result["png"]` |
88
89
  | `natal_report` **beta** | `calculateNatalReport` | birth; optional zodiac/houses/theme/SVG/download | `result["html"]`, source-linked `result["facts"]`, optional `result["download"]` |
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "astroapi-io-client"
7
- version = "0.2.3"
7
+ version = "0.2.4"
8
8
  description = "Synchronous Python client for the AstroAPI.io calculation gateway"
9
9
  readme = "README.md"
10
10
  license = "ISC"
@@ -17,7 +17,7 @@ from .types import (
17
17
  GeographicLocation, RelocationChartInput, AstrocartographyBody, AstrocartographyInput,
18
18
  EclipseType, EclipseGeometryInput, EclipseObserverSnapshot, EclipseEvent, EclipseGeometryResponse, MoonPhaseSector, ElectionalAspectFilter,
19
19
  ElectionalFilters, ElectionalSearchInput,
20
- EventRule, EventRuleSearchInput, ConventionProfile, ConventionCompareInput, ReleaseCompareInput, ChartRobustnessInput, CivilTimeAuditInput,
20
+ EventRule, EventRuleSearchInput, ConventionProfile, ConventionCompareInput, ReleaseCompareInput, ChartRobustnessInput, CivilTimeAuditInput, ChartInputReadinessExactTime, ChartInputReadinessApproximateTime, ChartInputReadinessUnknownTime, ChartInputReadinessBirthTime, ChartInputReadinessInput,
21
21
  NatalAnalysisAspectName, NatalAnalysisAspectOption, NatalAnalysisInput,
22
22
  AshtakootaInput, AshtakavargaInput, NumerologyInput, PlanetaryHoursInput,
23
23
  FixedStar, FixedStarsInput,
@@ -30,7 +30,7 @@ from .types import (
30
30
  PlaceSearchInput, TimezoneResolveInput, NatalContextInput,
31
31
  )
32
32
 
33
- __version__ = "0.2.3"
33
+ __version__ = "0.2.4"
34
34
  __all__ = [
35
35
  "NumerologyInput", "verify_calculation_receipt",
36
36
  "AstroAPIClient", "AstroAPIError", "ClientBusyError", "HTTPError", "DavisonInput",
@@ -45,7 +45,7 @@ __all__ = [
45
45
  "GeographicLocation", "RelocationChartInput", "AstrocartographyBody", "AstrocartographyInput",
46
46
  "EclipseType", "EclipseGeometryInput", "EclipseObserverSnapshot", "EclipseEvent", "EclipseGeometryResponse", "MoonPhaseSector", "ElectionalAspectFilter",
47
47
  "ElectionalFilters", "ElectionalSearchInput",
48
- "EventRule", "EventRuleSearchInput", "ConventionProfile", "ConventionCompareInput", "ReleaseCompareInput", "ChartRobustnessInput", "CivilTimeAuditInput",
48
+ "EventRule", "EventRuleSearchInput", "ConventionProfile", "ConventionCompareInput", "ReleaseCompareInput", "ChartRobustnessInput", "CivilTimeAuditInput", "ChartInputReadinessExactTime", "ChartInputReadinessApproximateTime", "ChartInputReadinessUnknownTime", "ChartInputReadinessBirthTime", "ChartInputReadinessInput",
49
49
  "NatalAnalysisAspectName", "NatalAnalysisAspectOption", "NatalAnalysisInput",
50
50
  "AshtakootaInput", "AshtakavargaInput", "PlanetaryHoursInput",
51
51
  "FixedStar", "FixedStarsInput",
@@ -676,6 +676,32 @@ def encode_request(route: str, payload: Any) -> bytes:
676
676
  _invalid("tropical profiles cannot include ayanamsa.")
677
677
  if profile["zodiac"] == "sidereal" and ("ayanamsa" not in profile or profile["ayanamsa"] not in _AYANAMSAS):
678
678
  _invalid("sidereal profiles require a supported ayanamsa.")
679
+ elif route == "chart-input-readiness":
680
+ _object(data, {"place_id", "birth_time", "profile"}, {"place_id", "birth_time", "profile"})
681
+ if type(data["place_id"]) is not str or re.fullmatch(r"geonames:[1-9][0-9]{0,11}:2026-10-02", data["place_id"]) is None:
682
+ _invalid("place_id must be a current versioned AstroAPI GeoNames place ID.")
683
+ workflow = _object(data["birth_time"], {"status", "date", "time", "uncertainty_minutes"}, {"status", "date"})
684
+ if workflow["status"] == "exact":
685
+ _object(workflow, {"status", "date", "time"}, {"status", "date", "time"})
686
+ _date(workflow["date"]); _time(workflow["time"])
687
+ elif workflow["status"] == "approximate":
688
+ _object(workflow, {"status", "date", "time", "uncertainty_minutes"}, {"status", "date", "time", "uncertainty_minutes"})
689
+ _date(workflow["date"]); _time(workflow["time"])
690
+ uncertainty = workflow["uncertainty_minutes"]
691
+ if type(uncertainty) is not int or not 5 <= uncertainty <= 180 or uncertainty % 5:
692
+ _invalid("chart input readiness uncertainty_minutes must be a multiple of 5 from 5 through 180.")
693
+ elif workflow["status"] == "unknown":
694
+ _object(workflow, {"status", "date"}, {"status", "date"})
695
+ _date(workflow["date"])
696
+ else:
697
+ _invalid("birth_time status must be exact, approximate, or unknown.")
698
+ profile = _object(data["profile"], {"zodiac", "ayanamsa", "house_system"}, {"zodiac", "house_system"})
699
+ _enum(profile["zodiac"], {"tropical", "sidereal"}, "zodiac must be tropical or sidereal.")
700
+ _enum(profile["house_system"], {"equal", "whole_sign", "placidus"}, "house_system is unsupported.")
701
+ if profile["zodiac"] == "tropical" and "ayanamsa" in profile:
702
+ _invalid("tropical profiles cannot include ayanamsa.")
703
+ if profile["zodiac"] == "sidereal" and ("ayanamsa" not in profile or profile["ayanamsa"] not in _AYANAMSAS):
704
+ _invalid("sidereal profiles require a supported ayanamsa.")
679
705
  elif route == "chart-robustness":
680
706
  _object(data, {"birth_time", "lat", "lon", "profiles"}, {"birth_time", "lat", "lon", "profiles"})
681
707
  workflow = _object(data["birth_time"], {"status", "date", "time", "timezone", "uncertainty_minutes"}, {"status"})
@@ -32,7 +32,7 @@ from .types import (
32
32
  MoonPhaseInput, MoonPhaseResponse, DavisonInput, PlanetaryEventsInput, PlanetaryEventsResponse,
33
33
  HouseIngressesInput, HouseIngressesResponse,
34
34
  AspectWindowsInput, AspectWindowsResponse,
35
- EventRuleSearchInput, ConventionCompareInput, ReleaseCompareInput, ChartRobustnessInput, CivilTimeAuditInput,
35
+ EventRuleSearchInput, ConventionCompareInput, ReleaseCompareInput, ChartRobustnessInput, CivilTimeAuditInput, ChartInputReadinessInput,
36
36
  EventCalendarInput, SynastrySvgInput, TransitSvgInput, NatalReportInput,
37
37
  BirthTimeSensitivityInput, RelocationChartInput, AstrocartographyInput,
38
38
  PlaceSearchInput, TimezoneResolveInput, NatalContextInput,
@@ -181,7 +181,7 @@ def _perform(
181
181
  "Content-Type": "application/json",
182
182
  "Accept": "application/json",
183
183
  "Accept-Encoding": "identity",
184
- "User-Agent": "astroapi-io-client/0.2.3",
184
+ "User-Agent": "astroapi-io-client/0.2.4",
185
185
  "Connection": "close",
186
186
  **({"X-AstroAPI-Receipt": "v1"} if request_receipt else {}),
187
187
  })
@@ -285,7 +285,7 @@ class AstroAPIClient:
285
285
 
286
286
  def _request(self, route: str, payload: object) -> JSONResponse:
287
287
  body = encode_request(route, payload)
288
- timeout = (self._timeline_timeout if route in {"nakshatra-timeline", "birth-time-sensitivity", "chart-robustness"}
288
+ timeout = (self._timeline_timeout if route in {"nakshatra-timeline", "birth-time-sensitivity", "chart-robustness", "chart-input-readiness"}
289
289
  else self._planetary_events_timeout if route in {"planetary-events", "house-ingresses", "eclipse-geometry", "electional-search", "event-rule-search"}
290
290
  else self._aspect_windows_timeout if route == "aspect-windows"
291
291
  else self._timeout)
@@ -432,6 +432,10 @@ class AstroAPIClient:
432
432
  """BETA: never guess a repeated or nonexistent local civil time."""
433
433
  return self._request("civil-time-audit", payload)
434
434
 
435
+ def chart_input_readiness(self, payload: ChartInputReadinessInput) -> JSONResponse:
436
+ """BETA: gate exact chart workflows against birth-time data quality."""
437
+ return self._request("chart-input-readiness", payload)
438
+
435
439
  def synastry_svg(self, payload: SynastrySvgInput) -> JSONResponse:
436
440
  """BETA: original deterministic double-wheel relationship SVG."""
437
441
  return self._request("synastry-svg", payload)
@@ -382,6 +382,33 @@ class CivilTimeAuditInput(TypedDict):
382
382
  profile: ConventionProfile
383
383
 
384
384
 
385
+ class ChartInputReadinessExactTime(TypedDict):
386
+ status: Literal["exact"]
387
+ date: str
388
+ time: str
389
+
390
+
391
+ class ChartInputReadinessApproximateTime(TypedDict):
392
+ status: Literal["approximate"]
393
+ date: str
394
+ time: str
395
+ uncertainty_minutes: int
396
+
397
+
398
+ class ChartInputReadinessUnknownTime(TypedDict):
399
+ status: Literal["unknown"]
400
+ date: str
401
+
402
+
403
+ ChartInputReadinessBirthTime = ChartInputReadinessExactTime | ChartInputReadinessApproximateTime | ChartInputReadinessUnknownTime
404
+
405
+
406
+ class ChartInputReadinessInput(TypedDict):
407
+ place_id: str
408
+ birth_time: ChartInputReadinessBirthTime
409
+ profile: ConventionProfile
410
+
411
+
385
412
  class ReleaseCompareInput(TypedDict):
386
413
  releases: list[JSONResponse]
387
414
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: astroapi-io-client
3
- Version: 0.2.3
3
+ Version: 0.2.4
4
4
  Summary: Synchronous Python client for the AstroAPI.io calculation gateway
5
5
  License-Expression: ISC
6
6
  Classifier: Programming Language :: Python :: 3
@@ -14,11 +14,11 @@ Dynamic: license-file
14
14
 
15
15
  # AstroAPI.io Python client
16
16
 
17
- The R196 source version `astroapi-io-client==0.2.3` covers all forty-four calculation routes: forty-three standard operations plus the separate Pro Nakshatra Timeline. Minor-body calculations are not part of the contract.
17
+ The R197 source version `astroapi-io-client==0.2.4` covers all forty-five calculation routes: forty-four standard operations plus the separate Pro Nakshatra Timeline. Minor-body calculations are not part of the contract.
18
18
 
19
19
  A synchronous client for Python 3.10+, with no runtime dependencies.
20
20
  The package import is `astroapi_client`; the distribution name is
21
- `astroapi-io-client`. Version 0.2.2 is the published R195 package; 0.2.3 is source-only and package availability never implies deployed API capabilities.
21
+ `astroapi-io-client`. Version 0.2.2 is the published R195 package; 0.2.4 is source-only and package availability never implies deployed API capabilities.
22
22
 
23
23
  Install from PyPI with `python -m pip install astroapi-io-client==0.2.2`, or from a local checkout using `python -m pip install ./clients/python`.
24
24
  Building needs setuptools and wheel; the installed client uses only the standard
@@ -97,6 +97,7 @@ Type hints describe the server contract and do not perform response validation.
97
97
  | `convention_compare` **beta** | `calculateConventionCompare` | two profiles with one birth, or two trusted signed responses | field-level differences without ranking or interpretation |
98
98
  | `chart_robustness` **beta** | `calculateChartRobustness` | unknown or approximate birth time plus 2–3 convention profiles | sampled stability classifications without rectification, ranking, or prediction |
99
99
  | `civil_time_audit` **beta** | `calculateCivilTimeAudit` | pinned place, local date/time, one explicit profile | both repeated-time chart candidates and exact 22-fact differences, or no shifted gap time |
100
+ | `chart_input_readiness` **beta** | `calculateChartInputReadiness` | pinned place, exact/approximate/unknown time quality, explicit profile | route readiness plus resolved, candidate, or sampled evidence for 22 facts |
100
101
  | `synastry_svg` **beta** | `calculateSynastrySvg` | two births; optional zodiac/houses/theme/aspects/`include_png` | `result["svg"]`, optional `result["png"]` |
101
102
  | `transit_svg` **beta** | `calculateTransitSvg` | birth and transit date; optional display controls/`include_png` | `result["svg"]`, optional `result["png"]` |
102
103
  | `natal_report` **beta** | `calculateNatalReport` | birth; optional zodiac/houses/theme/SVG/download | `result["html"]`, source-linked `result["facts"]`, optional `result["download"]` |
@@ -63,12 +63,14 @@ ROUTES += [
63
63
  RULE_INPUT = {"rule_version": 1, "start_utc": "2026-01-01T00:00:00Z", "end_utc": "2026-01-02T00:00:00Z", "location": {"lat": 0, "lon": 0}, "sample_minutes": 60, "rule": {"all": [{"fact": "planet_sign", "body": "moon", "value": "Taurus"}]}}
64
64
  CONVENTION_INPUT = {"birth": BIRTH, "profiles": [{"zodiac": "tropical", "house_system": "equal"}, {"zodiac": "sidereal", "ayanamsa": "lahiri", "house_system": "whole_sign"}]}
65
65
  CIVIL_TIME_AUDIT_INPUT = {"place_id": "geonames:5128581:2026-10-02", "date": "2024-11-03", "time": "01:30", "profile": {"zodiac": "tropical", "house_system": "equal"}}
66
+ CHART_INPUT_READINESS_INPUT = {"place_id": "geonames:5128581:2026-10-02", "birth_time": {"status": "exact", "date": "2024-11-03", "time": "01:30"}, "profile": {"zodiac": "tropical", "house_system": "equal"}}
66
67
  CHART_ROBUSTNESS_INPUT = {"birth_time": {"status": "approximate", "date": "2000-01-01", "time": "12:00", "timezone": "UTC", "uncertainty_minutes": 30}, "lat": 0, "lon": 0, "profiles": [{"zodiac": "tropical", "house_system": "equal"}, {"zodiac": "sidereal", "ayanamsa": "lahiri", "house_system": "whole_sign"}]}
67
68
  ROUTES += [
68
69
  ("event_rule_search", "event-rule-search", RULE_INPUT, {"type": "event_rule_search", "status": "beta", "rule_analysis": {}, "matching_samples": [], "sampled_candidate_windows": [], "metadata": {}}),
69
70
  ("convention_compare", "convention-compare", CONVENTION_INPUT, {"type": "convention_compare", "status": "beta", "profiles": [{}, {}], "differences": {}, "metadata": {}}),
70
71
  ("chart_robustness", "chart-robustness", CHART_ROBUSTNESS_INPUT, {"type": "chart_robustness", "status": "beta", "profiles": [{}, {}], "facts": [{} for _ in range(22)], "summary": {}, "evidence": {}, "metadata": {}}),
71
72
  ("civil_time_audit", "civil-time-audit", CIVIL_TIME_AUDIT_INPUT, {"type": "civil_time_audit", "status": "beta", "resolution": {}, "profile": {}, "calculation_ready": False, "requires_selection": True, "candidates": [], "comparison": {}, "metadata": {}}),
73
+ ("chart_input_readiness", "chart-input-readiness", CHART_INPUT_READINESS_INPUT, {"type": "chart_input_readiness", "status": "beta", "birth_time_status": "exact", "profile": {}, "resolution": {}, "exact_chart_ready": False, "next_action": "select_civil_time_candidate", "workflows": [{}, {}, {}, {}], "fact_evidence": [], "summary": {}, "evidence": {}, "metadata": {}}),
72
74
  ]
73
75
 
74
76
  @contextmanager
@@ -152,12 +154,14 @@ def local_client(url, **options):
152
154
  RULE_INPUT = {"rule_version": 1, "start_utc": "2026-01-01T00:00:00Z", "end_utc": "2026-01-02T00:00:00Z", "location": {"lat": 0, "lon": 0}, "sample_minutes": 60, "rule": {"all": [{"fact": "planet_sign", "body": "moon", "value": "Taurus"}]}}
153
155
  CONVENTION_INPUT = {"birth": BIRTH, "profiles": [{"zodiac": "tropical", "house_system": "equal"}, {"zodiac": "sidereal", "ayanamsa": "lahiri", "house_system": "whole_sign"}]}
154
156
  CIVIL_TIME_AUDIT_INPUT = {"place_id": "geonames:5128581:2026-10-02", "date": "2024-11-03", "time": "01:30", "profile": {"zodiac": "tropical", "house_system": "equal"}}
157
+ CHART_INPUT_READINESS_INPUT = {"place_id": "geonames:5128581:2026-10-02", "birth_time": {"status": "exact", "date": "2024-11-03", "time": "01:30"}, "profile": {"zodiac": "tropical", "house_system": "equal"}}
155
158
  CHART_ROBUSTNESS_INPUT = {"birth_time": {"status": "approximate", "date": "2000-01-01", "time": "12:00", "timezone": "UTC", "uncertainty_minutes": 30}, "lat": 0, "lon": 0, "profiles": [{"zodiac": "tropical", "house_system": "equal"}, {"zodiac": "sidereal", "ayanamsa": "lahiri", "house_system": "whole_sign"}]}
156
159
  ROUTES += [
157
160
  ("event_rule_search", "event-rule-search", RULE_INPUT, {"type": "event_rule_search", "status": "beta", "rule_analysis": {}, "matching_samples": [], "sampled_candidate_windows": [], "metadata": {}}),
158
161
  ("convention_compare", "convention-compare", CONVENTION_INPUT, {"type": "convention_compare", "status": "beta", "profiles": [{}, {}], "differences": {}, "metadata": {}}),
159
162
  ("chart_robustness", "chart-robustness", CHART_ROBUSTNESS_INPUT, {"type": "chart_robustness", "status": "beta", "profiles": [{}, {}], "facts": [{} for _ in range(22)], "summary": {}, "evidence": {}, "metadata": {}}),
160
163
  ("civil_time_audit", "civil-time-audit", CIVIL_TIME_AUDIT_INPUT, {"type": "civil_time_audit", "status": "beta", "resolution": {}, "profile": {}, "calculation_ready": False, "requires_selection": True, "candidates": [], "comparison": {}, "metadata": {}}),
164
+ ("chart_input_readiness", "chart-input-readiness", CHART_INPUT_READINESS_INPUT, {"type": "chart_input_readiness", "status": "beta", "birth_time_status": "exact", "profile": {}, "resolution": {}, "exact_chart_ready": False, "next_action": "select_civil_time_candidate", "workflows": [{}, {}, {}, {}], "fact_evidence": [], "summary": {}, "evidence": {}, "metadata": {}}),
161
165
  ]
162
166
 
163
167
  @contextmanager
@@ -415,6 +419,8 @@ class ClientTests(unittest.TestCase):
415
419
  client.chart_robustness({**CHART_ROBUSTNESS_INPUT, "profiles": [CHART_ROBUSTNESS_INPUT["profiles"][0]] * 2})
416
420
  with self.assertRaises(ValidationError):
417
421
  client.civil_time_audit({**CIVIL_TIME_AUDIT_INPUT, "place_id": "geonames:5128581:2020-01-01"})
422
+ with self.assertRaises(ValidationError):
423
+ client.chart_input_readiness({**CHART_INPUT_READINESS_INPUT, "birth_time": {"status": "approximate", "date": "2000-01-01", "time": "12:00"}})
418
424
 
419
425
 
420
426
  def test_request_body_limit(self):