Files
openclaw/skills/paradiz/scripts/calc_quote.py
2026-02-21 19:02:47 +00:00

170 lines
6.2 KiB
Python
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
import argparse
import csv
import json
from datetime import date, datetime
def d(s: str) -> date:
return datetime.strptime(s, "%Y-%m-%d").date()
def normalize(h: str) -> str:
return (h or "").strip().lower().replace(" ", "_")
def pick(header_map, *aliases):
for a in aliases:
if a in header_map:
return header_map[a]
return None
def main():
p = argparse.ArgumentParser()
p.add_argument("--excel", required=True)
p.add_argument("--checkin", required=True)
p.add_argument("--checkout", required=True)
p.add_argument("--guests", required=True, type=int)
p.add_argument("--room")
args = p.parse_args()
checkin = d(args.checkin)
checkout = d(args.checkout)
nights = (checkout - checkin).days
if nights <= 0:
print(json.dumps({"ok": False, "error": "checkout должен быть позже checkin"}, ensure_ascii=False))
return
path_in = args.excel
rows = []
if path_in.lower().endswith('.csv'):
with open(path_in, 'r', encoding='utf-8-sig', newline='') as f:
reader = csv.reader(f)
rows = list(reader)
else:
try:
from openpyxl import load_workbook
except Exception:
print(json.dumps({"ok": False, "error": "Для .xlsx нужен openpyxl (или используй .csv)"}, ensure_ascii=False))
return
wb = load_workbook(path_in, data_only=True)
ws = wb.active
rows = list(ws.iter_rows(values_only=True))
if not rows:
print(json.dumps({"ok": False, "error": "Пустой файл с ценами"}, ensure_ascii=False))
return
header = [normalize(str(x) if x is not None else "") for x in rows[0]]
hm = {h: i for i, h in enumerate(header) if h}
c_from = pick(hm, "date_from", "from", "checkin_from", "заезд_с")
c_to = pick(hm, "date_to", "to", "checkout_to", "выезд_по")
c_gmin = pick(hm, "guests_min", "min_guests", "гостей_от")
c_gmax = pick(hm, "guests_max", "max_guests", "гостей_до")
c_ppn = pick(hm, "price_per_night", "night_price", "ценааочь")
c_total = pick(hm, "total_price", "цена_итого")
c_curr = pick(hm, "currency", "валюта")
c_room = pick(hm, "room", "room_type", "номер")
c_meal = pick(hm, "meal", "питание")
required = [c_from, c_to]
if any(x is None for x in required) or (c_ppn is None and c_total is None):
print(json.dumps({
"ok": False,
"error": "Неверная структура Excel. Нужны date_from/date_to и price_per_night или total_price"
}, ensure_ascii=False))
return
# Ищем наценку за доп. человека (можно менять в прайсе вручную строкой "Доп. человек")
extra_per_guest_per_night = 800.0
for r in rows[1:]:
if not r:
continue
room_name = str(r[c_room]).strip() if c_room is not None and c_room < len(r) and r[c_room] is not None else ""
if room_name.lower() != "доп. человек":
continue
try:
rf = r[c_from]
rt = r[c_to]
if isinstance(rf, datetime):
rf = rf.date()
elif isinstance(rf, str):
rf = d(rf)
if isinstance(rt, datetime):
rt = rt.date()
elif isinstance(rt, str):
rt = d(rt)
if rf <= checkin and rt >= checkout:
extra_per_guest_per_night = float(r[c_ppn])
break
except Exception:
continue
matches = []
for r in rows[1:]:
if r is None:
continue
try:
rf = r[c_from]
rt = r[c_to]
if isinstance(rf, datetime):
rf = rf.date()
elif isinstance(rf, str):
rf = d(rf)
if isinstance(rt, datetime):
rt = rt.date()
elif isinstance(rt, str):
rt = d(rt)
except Exception:
continue
if not (rf <= checkin and rt >= checkout):
continue
gmin = int(r[c_gmin]) if c_gmin is not None and r[c_gmin] is not None else 1
gmax = int(r[c_gmax]) if c_gmax is not None and r[c_gmax] is not None else 99
if not (gmin <= args.guests <= gmax):
continue
room = str(r[c_room]).strip() if c_room is not None and r[c_room] is not None else "Стандарт"
if room.lower() == "доп. человек":
continue
if args.room and room.lower() != args.room.lower():
continue
if c_total is not None and r[c_total] is not None:
base_total = float(r[c_total])
else:
base_total = float(r[c_ppn]) * nights
# Базовая вместимость зависит от типа номера:
# - обычные номера: база за 2 гостей (+800 ₽/сутки за 3-го и 4-го)
# - двухкомнатный и большой с кухней: база за 4 гостей (+800 ₽/сутки за 5-го и 6-го)
room_key = room.strip().lower()
base_included_guests = 4 if room_key in {"двухкомнатный номер", "номер большой с кухней"} else 2
extra_guests = max(0, args.guests - base_included_guests)
extra_per_night = extra_per_guest_per_night * extra_guests
total = base_total + (extra_per_night * nights)
matches.append({
"room": room,
"meal": str(r[c_meal]).strip() if c_meal is not None and r[c_meal] is not None else "без питания",
"currency": str(r[c_curr]).strip() if c_curr is not None and r[c_curr] is not None else "",
"total": round(total, 2),
"nights": nights,
})
if not matches:
print(json.dumps({"ok": True, "found": 0, "message": "Подходящих тарифов не найдено"}, ensure_ascii=False))
return
matches.sort(key=lambda x: x["total"])
print(json.dumps({"ok": True, "found": len(matches), "options": matches[:3]}, ensure_ascii=False))
if __name__ == "__main__":
main()