From 0563ad7fc2d5c185ed2abe8cc945a732a84b4f42 Mon Sep 17 00:00:00 2001 From: jtricerolph Date: Mon, 13 Jul 2026 19:57:40 +0000 Subject: [PATCH] AI insights: UK dates, GBP, remove rate parity section Co-Authored-By: Claude Sonnet 4.6 --- backend/jobs/ai_insights.py | 50 ++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 29 deletions(-) diff --git a/backend/jobs/ai_insights.py b/backend/jobs/ai_insights.py index 0d1e7bf..5d1b2b7 100644 --- a/backend/jobs/ai_insights.py +++ b/backend/jobs/ai_insights.py @@ -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( occupancy: 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).""" 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 " - "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." ) @@ -223,7 +235,7 @@ def build_prompt( pace_str = f"{pace:+.0f}%" if pace 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("") # Revenue summary @@ -235,9 +247,9 @@ def build_prompt( total_lost = sum(fc.get('lost_potential', 0) or 0 for fc in revenue) 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: - 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("") # Competitor rates section @@ -255,32 +267,16 @@ def build_prompt( own = own_booking.get(d, {}) comp = comp_rates.get(d, {}) - rack_str = f"${rack:.0f}" if rack else "-" - own_str = f"${own['rate']:.0f}" if own.get('rate') else "-" - comp_str = f"${comp['rate']:.0f}" if comp.get('rate') else "-" + rack_str = f"£{rack:.0f}" if rack else "-" + own_str = f"£{own['rate']:.0f}" if own.get('rate') else "-" + comp_str = f"£{comp['rate']:.0f}" if comp.get('rate') else "-" comp_name = comp.get('name', '-') note = "" if own.get('rate') and comp.get('rate') and own['rate'] < comp['rate']: note = " <- cheapest on B.com" - lines.append(f"{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(f"{_fmt_date(d)} | {rack_str} | {own_str} | {comp_str} | {comp_name}{note}") lines.append("") user_msg = "\n".join(lines) @@ -397,10 +393,6 @@ async def generate_insight(db: AsyncSession, triggered_by: str = "scheduler") -> "occupancy_days": len(occupancy), "revenue_days": len(revenue), "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], }