|
@@ -68,12 +68,13 @@ def _parse_price(s: str) -> float | None:
|
|
|
return None
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
|
-def _parse_booked_nets(det: dict, check_in: str, check_out: str) -> dict[str, int]:
|
|
|
|
|
|
|
+def _parse_booked_nets(det: dict, check_in: str, check_out: str) -> dict[str, float]:
|
|
|
"""Return {YYYY-MM-DD: net_dollars} for each night of a reservation.
|
|
"""Return {YYYY-MM-DD: net_dollars} for each night of a reservation.
|
|
|
|
|
|
|
|
Uses proportional apportionment: net_i = total_host_net * (list_i / sum(list)).
|
|
Uses proportional apportionment: net_i = total_host_net * (list_i / sum(list)).
|
|
|
Reads PAYMENTINFO_SECTION from stayHostingDetails (rootPlacement).
|
|
Reads PAYMENTINFO_SECTION from stayHostingDetails (rootPlacement).
|
|
|
Returns {} if the section is missing or the data is malformed.
|
|
Returns {} if the section is missing or the data is malformed.
|
|
|
|
|
+ Values are unrounded floats; CalPrice.set rounds via base=round(base).
|
|
|
"""
|
|
"""
|
|
|
sections = det.get('rootPlacement', [])
|
|
sections = det.get('rootPlacement', [])
|
|
|
pay = next((s for s in sections if s.get('sectionId') == 'PAYMENTINFO_SECTION'), None)
|
|
pay = next((s for s in sections if s.get('sectionId') == 'PAYMENTINFO_SECTION'), None)
|
|
@@ -105,7 +106,7 @@ def _parse_booked_nets(det: dict, check_in: str, check_out: str) -> dict[str, in
|
|
|
total_weight = sum(weights)
|
|
total_weight = sum(weights)
|
|
|
if total_weight == 0:
|
|
if total_weight == 0:
|
|
|
return {}
|
|
return {}
|
|
|
- return {d: round(total_net * w / total_weight) for d, w in zip(nights, weights)}
|
|
|
|
|
|
|
+ return {d: total_net * w / total_weight for d, w in zip(nights, weights)}
|
|
|
|
|
|
|
|
|
|
|
|
|
class AirbnbAuthError(ServiceAuthError):
|
|
class AirbnbAuthError(ServiceAuthError):
|
|
@@ -136,7 +137,7 @@ class _AirbnbClient:
|
|
|
self._refresh_task: asyncio.Task | None = None
|
|
self._refresh_task: asyncio.Task | None = None
|
|
|
self._http = httpx.AsyncClient(timeout=10.0)
|
|
self._http = httpx.AsyncClient(timeout=10.0)
|
|
|
self._detail_cache: dict[str, dict] = {}
|
|
self._detail_cache: dict[str, dict] = {}
|
|
|
- self._booked_nets: dict[str, int] = {}
|
|
|
|
|
|
|
+ self._booked_nets: dict[str, float] = {}
|
|
|
|
|
|
|
|
@classmethod
|
|
@classmethod
|
|
|
def from_env(cls) -> '_AirbnbClient':
|
|
def from_env(cls) -> '_AirbnbClient':
|
|
@@ -447,7 +448,7 @@ class _AirbnbClient:
|
|
|
key=lambda r: r.check_in,
|
|
key=lambda r: r.check_in,
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
- async def get_booked_nets(self, date_from: str, date_to: str) -> dict[str, int | None]:
|
|
|
|
|
|
|
+ async def get_booked_nets(self, date_from: str, date_to: str) -> dict[str, float | None]:
|
|
|
"""Return {date: net_dollars} for Airbnb-booked dates in range.
|
|
"""Return {date: net_dollars} for Airbnb-booked dates in range.
|
|
|
|
|
|
|
|
Fetches StayHostingDetailsQuery for each reservation and apportions total
|
|
Fetches StayHostingDetailsQuery for each reservation and apportions total
|
|
@@ -489,7 +490,7 @@ class _AirbnbClient:
|
|
|
self._booked_nets.update(_parse_booked_nets(det, ci, co))
|
|
self._booked_nets.update(_parse_booked_nets(det, ci, co))
|
|
|
|
|
|
|
|
await asyncio.gather(*[fetch_nets(code) for code in set(booked_codes.values())])
|
|
await asyncio.gather(*[fetch_nets(code) for code in set(booked_codes.values())])
|
|
|
- return {d: self._booked_nets.get(d) for d in booked_codes}
|
|
|
|
|
|
|
+ return {d: self._booked_nets.get(d) for d in booked_codes} # float | None per date
|
|
|
|
|
|
|
|
async def set_price(self, d: str, price: int) -> None:
|
|
async def set_price(self, d: str, price: int) -> None:
|
|
|
price = int(round(price))
|
|
price = int(round(price))
|