AI insights: UK dates, GBP, remove rate parity section
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
9761e88e9b
commit
0563ad7fc2
1 changed files with 21 additions and 29 deletions
|
|
@ -186,6 +186,17 @@ async def gather_competitor_data(db: AsyncSession, days: int = 14) -> Dict[str,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _fmt_date(d: str) -> str:
|
||||||
|
"""Convert YYYY-MM-DD to DD/MM/YYYY for UK display."""
|
||||||
|
try:
|
||||||
|
parts = str(d).split('-')
|
||||||
|
if len(parts) == 3:
|
||||||
|
return f"{parts[2]}/{parts[1]}/{parts[0]}"
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return str(d)
|
||||||
|
|
||||||
|
|
||||||
def build_prompt(
|
def build_prompt(
|
||||||
occupancy: List[Dict],
|
occupancy: List[Dict],
|
||||||
revenue: List[Dict],
|
revenue: List[Dict],
|
||||||
|
|
@ -195,9 +206,10 @@ def build_prompt(
|
||||||
"""Build system and user prompts from gathered data. Returns (system_msg, user_msg)."""
|
"""Build system and user prompts from gathered data. Returns (system_msg, user_msg)."""
|
||||||
|
|
||||||
system_msg = (
|
system_msg = (
|
||||||
"You are an AI assistant for a hotel revenue manager. Analyze the data below and provide "
|
"You are an AI assistant for a hotel revenue manager in the UK. Analyze the data below and provide "
|
||||||
"a concise daily briefing (3-5 bullet points). Focus on: occupancy trends, pace vs prior "
|
"a concise daily briefing (3-5 bullet points). Focus on: occupancy trends, pace vs prior "
|
||||||
"year, pricing opportunities, rate parity issues, and anything unusual requiring attention. "
|
"year, pricing opportunities, and anything unusual requiring attention. "
|
||||||
|
"Use UK date format (DD/MM/YYYY) and GBP (£) for all monetary values. "
|
||||||
"Be specific with numbers and dates. Keep it actionable — no fluff or generic advice."
|
"Be specific with numbers and dates. Keep it actionable — no fluff or generic advice."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -223,7 +235,7 @@ def build_prompt(
|
||||||
pace_str = f"{pace:+.0f}%" if pace is not None else "-"
|
pace_str = f"{pace:+.0f}%" if pace is not None else "-"
|
||||||
bud_str = f"{budget_val:.0f}%" if budget_val is not None else "-"
|
bud_str = f"{budget_val:.0f}%" if budget_val is not None else "-"
|
||||||
|
|
||||||
lines.append(f"{d} | {dow} | {otb_str} | {fc_str} | {py_str} | {pace_str} | {bud_str}")
|
lines.append(f"{_fmt_date(d)} | {dow} | {otb_str} | {fc_str} | {py_str} | {pace_str} | {bud_str}")
|
||||||
lines.append("")
|
lines.append("")
|
||||||
|
|
||||||
# Revenue summary
|
# Revenue summary
|
||||||
|
|
@ -235,9 +247,9 @@ def build_prompt(
|
||||||
total_lost = sum(fc.get('lost_potential', 0) or 0 for fc in revenue)
|
total_lost = sum(fc.get('lost_potential', 0) or 0 for fc in revenue)
|
||||||
|
|
||||||
lines.append("## Revenue Signals")
|
lines.append("## Revenue Signals")
|
||||||
lines.append(f"14-day forecast: ${total_forecast:,.0f} | OTB: ${total_otb:,.0f} | PY: ${total_py:,.0f}")
|
lines.append(f"14-day forecast: £{total_forecast:,.0f} | OTB: £{total_otb:,.0f} | PY: £{total_py:,.0f}")
|
||||||
if opportunity_days > 0:
|
if opportunity_days > 0:
|
||||||
lines.append(f"Pricing opportunity days: {opportunity_days} | Total lost potential: ${total_lost:,.0f}")
|
lines.append(f"Pricing opportunity days: {opportunity_days} | Total lost potential: £{total_lost:,.0f}")
|
||||||
lines.append("")
|
lines.append("")
|
||||||
|
|
||||||
# Competitor rates section
|
# Competitor rates section
|
||||||
|
|
@ -255,32 +267,16 @@ def build_prompt(
|
||||||
own = own_booking.get(d, {})
|
own = own_booking.get(d, {})
|
||||||
comp = comp_rates.get(d, {})
|
comp = comp_rates.get(d, {})
|
||||||
|
|
||||||
rack_str = f"${rack:.0f}" if rack else "-"
|
rack_str = f"£{rack:.0f}" if rack else "-"
|
||||||
own_str = f"${own['rate']:.0f}" if own.get('rate') else "-"
|
own_str = f"£{own['rate']:.0f}" if own.get('rate') else "-"
|
||||||
comp_str = f"${comp['rate']:.0f}" if comp.get('rate') else "-"
|
comp_str = f"£{comp['rate']:.0f}" if comp.get('rate') else "-"
|
||||||
comp_name = comp.get('name', '-')
|
comp_name = comp.get('name', '-')
|
||||||
|
|
||||||
note = ""
|
note = ""
|
||||||
if own.get('rate') and comp.get('rate') and own['rate'] < comp['rate']:
|
if own.get('rate') and comp.get('rate') and own['rate'] < comp['rate']:
|
||||||
note = " <- cheapest on B.com"
|
note = " <- cheapest on B.com"
|
||||||
|
|
||||||
lines.append(f"{d} | {rack_str} | {own_str} | {comp_str} | {comp_name}{note}")
|
lines.append(f"{_fmt_date(d)} | {rack_str} | {own_str} | {comp_str} | {comp_name}{note}")
|
||||||
lines.append("")
|
|
||||||
|
|
||||||
# Rate parity flags
|
|
||||||
parity_flags = []
|
|
||||||
for d in sorted(rack_rates.keys()):
|
|
||||||
rack = rack_rates.get(d)
|
|
||||||
own = own_booking.get(d, {})
|
|
||||||
if rack and own.get('rate') and rack > 0:
|
|
||||||
delta_pct = ((own['rate'] - rack) / rack) * 100
|
|
||||||
if abs(delta_pct) > 5:
|
|
||||||
parity_flags.append(f"{d}: Rack ${rack:.0f} vs B.com ${own['rate']:.0f} ({delta_pct:+.1f}%)")
|
|
||||||
|
|
||||||
if parity_flags:
|
|
||||||
lines.append("## Rate Parity Flags (own rack vs own Booking.com, >5% delta)")
|
|
||||||
for flag in parity_flags:
|
|
||||||
lines.append(flag)
|
|
||||||
lines.append("")
|
lines.append("")
|
||||||
|
|
||||||
user_msg = "\n".join(lines)
|
user_msg = "\n".join(lines)
|
||||||
|
|
@ -397,10 +393,6 @@ async def generate_insight(db: AsyncSession, triggered_by: str = "scheduler") ->
|
||||||
"occupancy_days": len(occupancy),
|
"occupancy_days": len(occupancy),
|
||||||
"revenue_days": len(revenue),
|
"revenue_days": len(revenue),
|
||||||
"competitor_dates": len(competitor.get('own_booking', {})),
|
"competitor_dates": len(competitor.get('own_booking', {})),
|
||||||
"parity_flags": sum(1 for d in competitor.get('rack', {})
|
|
||||||
if competitor.get('own_booking', {}).get(d, {}).get('rate')
|
|
||||||
and competitor['rack'].get(d)
|
|
||||||
and abs((competitor['own_booking'][d]['rate'] - competitor['rack'][d]) / competitor['rack'][d] * 100) > 5),
|
|
||||||
"prompt_preview": user_msg[:500],
|
"prompt_preview": user_msg[:500],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue