△
DMCallv1
Projects
AI Models
Docs
Inventory
Rules
Backup
AI Logs
Workspaces
Projects
/ Booking Monitor 03/2026 / Edit
Edit Project
Basic Info
Project Name *
Slug *
URL-safe identifier, auto-generated from name if blank.
Description
Status
Active
Inactive
Archived
Tech Stack
Tags
Internal Notes
# bMonV3_2 - Session Log (2026-03-07) ## Overview Full setup session covering: report fixes, nightly fetch automation, and daily AI email digest system. --- ## 1. Report Pricing Fixes (`reportsV3/report_pricing_v1.php`) ### Tab Loading Fix - **Problem**: Only the Occupancy Rate tab loaded; all other tabs returned 0 bytes via AJAX. - **Root cause**: Deeply nested `if/endif` blocks (`$loadError` if/elseif/else inside `!$isAjaxRequest`) made AJAX splitting impossible. - **Fix**: Changed `shouldRenderTab()` to render ALL tabs on initial page load (no lazy loading). AJAX still works for date filter changes. ### Top Navigation Menu - Added `nav.php` include after `<body>` tag for consistent site navigation. ### Default Date Ranges Changed - Check-in: today to **+3 days** (was +30 days) - Query Date: past **3 days** (was 14 days) ```php $defaultCiTo = date('Y-m-d', strtotime('+3 days')); $defaultQdFrom = date('Y-m-d', strtotime($defaultQdTo . ' -3 days')); ``` --- ## 2. Dashboard 500 Error Fix (`fetch_v2026/dashboard.php`) - **Endpoint**: `?action=recent_failures&limit=50` - **Error**: `$in needs an array` — PHP's `array_unique()` preserves keys, creating associative array. - **Fix**: Wrapped with `array_values()`: ```php $hotelIds = array_values(array_unique(array_column($failures, 'hotel_id'))); ``` --- ## 3. Nightly Fetch Automation ### Script: `fetch_v2026/nightlyFetch.sh` - Schedules jobs via `jobScheduler_propsid.php` (today + 30 days) - Checks browser pool health at `http://127.0.0.1:3100/health` - Launches **9 parallel workers** via `jobWorker.sh` - Waits for all workers, logs results ### Cron (2:00 AM daily): ``` 0 2 * * * /var/www/__slpx2/www/mssql.danmarcrm.com/dev1/bMonV3_2/fetch_v2026/nightlyFetch.sh >> /var/www/__slpx2/www/mssql.danmarcrm.com/dev1/bMonV3_2/fetch_v2026/logs/cron_nightly.log 2>&1 ``` ### Worker count: 9 (default in nightlyFetch.sh) --- ## 4. Daily AI Email Digest ### Files Created | File | Purpose | |------|---------| | `fetch_v2026/daily_digest.php` | Main script: gathers data, calls AI, sends email | | `fetch_v2026/daily_digest_config.php` | All configuration (editable without touching main script) | ### Config Highlights (`daily_digest_config.php`) ```php 'email' => [ 'to' => 'dan@danmarcrm.com', 'from' => 'booking_monitor@crm.place', 'from_name' => 'bMon Daily Digest', 'subject' => 'bMon Daily Report - {date}', 'sendgrid_api_key' => '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', ], 'ai' => [ 'provider' => 'claude', 'model' => null, // uses default from configIndex.php 'max_tokens' => 4000, ], 'data' => [ 'query_days_back' => 3, // past 3 days of fetch data 'checkin_days_ahead' => 7, // next 7 days of bookings 'market_top_n' => 30, // top 30 cheapest hotels in detail ], ``` ### Configurable Data Sections (toggle on/off in config) | Section | Description | |---------|-------------| | `zenith_detail` | 4 Zenith properties: per-room prices, availability evolution | | `market_summary` | All hotels aggregated: occupancy, avg/min/max prices per checkin | | `market_hotels` | Top 30 cheapest hotels with prices per checkin date | | `market_occupancy` | Per-checkin: total hotels, available, sold-out % | | `booking_detection` | Zenith rooms that changed availability (booking/cancellation) | | `fetch_stats` | Fetch job success/failure counts | ### System & User Prompts - Both fully editable in config file - `system_prompt`: Revenue manager role, format instructions (Key Highlights, Urgent Actions, Pricing Recommendations, etc.) - `user_prompt`: Template with `{data}`, `{date}`, `{query_days_back}`, `{checkin_days_ahead}` placeholders ### CLI Flags ```bash php8.3 daily_digest.php # Full run: AI + email + MongoDB save php8.3 daily_digest.php --dry-run # Data gathering only, no AI call php8.3 daily_digest.php --no-email # AI call + MongoDB save, skip email php8.3 daily_digest.php --stdout # Print AI analysis to terminal ``` ### Cron (8:00 AM daily): ``` 0 8 * * * /usr/bin/php8.3 /var/www/__slpx2/www/mssql.danmarcrm.com/dev1/bMonV3_2/fetch_v2026/daily_digest.php >> /var/www/__slpx2/www/mssql.danmarcrm.com/dev1/bMonV3_2/fetch_v2026/logs/cron_digest.log 2>&1 ``` ### MongoDB Storage - Collection: `price_ai_analyses` - Fields: `source`, `query_date`, `analysis`, `prompt`, `system_prompt`, `model`, `sections_included`, `data_stats`, `created_at` --- ## 5. Key Technical Discoveries ### MongoDB `room_details` Field Types | Field | Type | Example | Notes | |-------|------|---------|-------| | `price` | STRING | `"€ 92"` | Display format, NOT numeric | | `raw_price` | STRING | `"92.00"` | Numeric string | | `avg_price_per_night_eur` | STRING | `"92.00"` | Best field for price — use `$convert` to double | | `available_units` | INT | `2` | Correct availability field | | `nr_left` | often NULL | | DO NOT USE for availability | | `room_name` | STRING | `"No Availability"` | Filter these out (price=0 placeholders) | ### Price Conversion in MongoDB Aggregation ```php 'price_num' => ['$convert' => [ 'input' => '$avg_price_per_night_eur', 'to' => 'double', 'onError' => 0, 'onNull' => 0 ]] ``` ### Two-Stage Aggregation Pattern (for market stats) 1. Stage 1: Group by `hotel_id + checkin` → get per-hotel min price 2. Stage 2: Group by `checkin` → get market avg/min/max across hotels --- ## 6. Full Crontab (as of 2026-03-07) ```cron ## bMonV3_2 booking fetch + daily digest 0 2 * * * /var/www/__slpx2/www/mssql.danmarcrm.com/dev1/bMonV3_2/fetch_v2026/nightlyFetch.sh >> .../logs/cron_nightly.log 2>&1 0 8 * * * /usr/bin/php8.3 /var/www/__slpx2/www/mssql.danmarcrm.com/dev1/bMonV3_2/fetch_v2026/daily_digest.php >> .../logs/cron_digest.log 2>&1 ``` --- ## 7. Key File Paths | Path | Description | |------|-------------| | `fetch_v2026/daily_digest.php` | Daily AI digest main script | | `fetch_v2026/daily_digest_config.php` | Digest configuration (prompts, email, sections) | | `fetch_v2026/nightlyFetch.sh` | Nightly fetch orchestrator | | `fetch_v2026/jobScheduler_propsid.php` | Creates fetch jobs from propsid table | | `fetch_v2026/jobWorker.sh` | Individual worker (claims + processes jobs) | | `fetch_v2026/getHotelAndRoomDetailsPool.php` | Scraper using browser pool | | `fetch_v2026/dashboard.php` | Fetch monitoring dashboard | | `fetch_v2026/config.php` | Fetch system configuration | | `fetch_v2026/pool/` | Browser pool service (PM2) | | `fetch_v2026/logs/` | All log files | | `reportsV3/report_pricing_v1.php` | Pricing reports dashboard | | `mongodb/MongoConnection.php` | MongoDB wrapper class | --- ## 8. Test Results (2026-03-07 21:03) Full pipeline test (`--stdout`): - **Data**: 30 hotels, 8 checkin dates, 204 hotels in propsid, 30,814 char prompt - **AI model**: claude-sonnet-4-20250514 - **AI response**: 1,946 chars - **MongoDB**: Saved to `price_ai_analyses` - **Email**: Sent via SendGrid (HTTP 202) to dan@danmarcrm.com - **Note**: Zenith showed no data because last fetch was 2026-02-28 (outside 3-day window). After tonight's 2 AM fetch, the 8 AM digest will include fresh Zenith data. --- ## 9. Infrastructure Notes - **PHP**: 8.3 (`/usr/bin/php8.3`) - **MongoDB**: Local, database `booking_analysis`, port 27017 - **Browser Pool**: PM2-managed, 20 browsers, port 3100, local only - **Zenith Hotel IDs**: 13613044, 15081492, 15046719, 13121164 - **Server**: `/var/www/__slpx2/www/mssql.danmarcrm.com/dev1/bMonV3_2` - **URL**: `https://bmonv3-2.danmarcrm.com` // after compactation # bMonV3_2 - Session Log (2026-03-07) ## Overview Full setup session covering: report fixes, nightly fetch automation, and daily AI email digest system. --- ## 1. Report Pricing Fixes (`reportsV3/report_pricing_v1.php`) ### Tab Loading Fix - **Problem**: Only the Occupancy Rate tab loaded; all other tabs returned 0 bytes via AJAX. - **Root cause**: Deeply nested `if/endif` blocks (`$loadError` if/elseif/else inside `!$isAjaxRequest`) made AJAX splitting impossible. - **Fix**: Changed `shouldRenderTab()` to render ALL tabs on initial page load (no lazy loading). AJAX still works for date filter changes. ### Top Navigation Menu - Added `nav.php` include after `<body>` tag for consistent site navigation. ### Default Date Ranges Changed - Check-in: today to **+3 days** (was +30 days) - Query Date: past **3 days** (was 14 days) ```php $defaultCiTo = date('Y-m-d', strtotime('+3 days')); $defaultQdFrom = date('Y-m-d', strtotime($defaultQdTo . ' -3 days')); ``` --- ## 2. Dashboard 500 Error Fix (`fetch_v2026/dashboard.php`) - **Endpoint**: `?action=recent_failures&limit=50` - **Error**: `$in needs an array` — PHP's `array_unique()` preserves keys, creating associative array. - **Fix**: Wrapped with `array_values()`: ```php $hotelIds = array_values(array_unique(array_column($failures, 'hotel_id'))); ``` --- ## 3. Nightly Fetch Automation ### Script: `fetch_v2026/nightlyFetch.sh` - Schedules jobs via `jobScheduler_propsid.php` (today + 30 days) - Checks browser pool health at `http://127.0.0.1:3100/health` - Launches **9 parallel workers** via `jobWorker.sh` - Waits for all workers, logs results ### Cron (2:00 AM daily): ``` 0 2 * * * /var/www/__slpx2/www/mssql.danmarcrm.com/dev1/bMonV3_2/fetch_v2026/nightlyFetch.sh >> /var/www/__slpx2/www/mssql.danmarcrm.com/dev1/bMonV3_2/fetch_v2026/logs/cron_nightly.log 2>&1 ``` ### Worker count: 9 (default in nightlyFetch.sh) --- ## 4. Daily AI Email Digest ### Files Created | File | Purpose | |------|---------| | `fetch_v2026/daily_digest.php` | Main script: gathers data, calls AI, sends email | | `fetch_v2026/daily_digest_config.php` | All configuration (editable without touching main script) | ### Config Highlights (`daily_digest_config.php`) ```php 'email' => [ 'to' => 'dan@danmarcrm.com', 'from' => 'booking_monitor@crm.place', 'from_name' => 'bMon Daily Digest', 'subject' => 'bMon Daily Report - {date}', 'sendgrid_api_key' => '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', ], 'ai' => [ 'provider' => 'claude', 'model' => null, // uses default from configIndex.php 'max_tokens' => 4000, ], 'data' => [ 'query_days_back' => 3, // past 3 days of fetch data 'checkin_days_ahead' => 7, // next 7 days of bookings 'market_top_n' => 30, // top 30 cheapest hotels in detail ], ``` ### Configurable Data Sections (toggle on/off in config) | Section | Description | |---------|-------------| | `zenith_detail` | 4 Zenith properties: per-room prices, availability evolution | | `market_summary` | All hotels aggregated: occupancy, avg/min/max prices per checkin | | `market_hotels` | Top 30 cheapest hotels with prices per checkin date | | `market_occupancy` | Per-checkin: total hotels, available, sold-out % | | `booking_detection` | Zenith rooms that changed availability (booking/cancellation) | | `fetch_stats` | Fetch job success/failure counts | ### System & User Prompts - Both fully editable in config file - `system_prompt`: Revenue manager role, format instructions (Key Highlights, Urgent Actions, Pricing Recommendations, etc.) - `user_prompt`: Template with `{data}`, `{date}`, `{query_days_back}`, `{checkin_days_ahead}` placeholders ### CLI Flags ```bash php8.3 daily_digest.php # Full run: AI + email + MongoDB save php8.3 daily_digest.php --dry-run # Data gathering only, no AI call php8.3 daily_digest.php --no-email # AI call + MongoDB save, skip email php8.3 daily_digest.php --stdout # Print AI analysis to terminal ``` ### Cron (8:00 AM daily): ``` 0 8 * * * /usr/bin/php8.3 /var/www/__slpx2/www/mssql.danmarcrm.com/dev1/bMonV3_2/fetch_v2026/daily_digest.php >> /var/www/__slpx2/www/mssql.danmarcrm.com/dev1/bMonV3_2/fetch_v2026/logs/cron_digest.log 2>&1 ``` ### MongoDB Storage - Collection: `price_ai_analyses` - Fields: `source`, `query_date`, `analysis`, `prompt`, `system_prompt`, `model`, `sections_included`, `data_stats`, `created_at` --- ## 5. Key Technical Discoveries ### MongoDB `room_details` Field Types | Field | Type | Example | Notes | |-------|------|---------|-------| | `price` | STRING | `"€ 92"` | Display format, NOT numeric | | `raw_price` | STRING | `"92.00"` | Numeric string | | `avg_price_per_night_eur` | STRING | `"92.00"` | Best field for price — use `$convert` to double | | `available_units` | INT | `2` | Correct availability field | | `nr_left` | often NULL | | DO NOT USE for availability | | `room_name` | STRING | `"No Availability"` | Filter these out (price=0 placeholders) | ### Price Conversion in MongoDB Aggregation ```php 'price_num' => ['$convert' => [ 'input' => '$avg_price_per_night_eur', 'to' => 'double', 'onError' => 0, 'onNull' => 0 ]] ``` ### Two-Stage Aggregation Pattern (for market stats) 1. Stage 1: Group by `hotel_id + checkin` → get per-hotel min price 2. Stage 2: Group by `checkin` → get market avg/min/max across hotels --- ## 6. Full Crontab (as of 2026-03-07) ```cron ## bMonV3_2 booking fetch + daily digest 0 2 * * * /var/www/__slpx2/www/mssql.danmarcrm.com/dev1/bMonV3_2/fetch_v2026/nightlyFetch.sh >> .../logs/cron_nightly.log 2>&1 0 8 * * * /usr/bin/php8.3 /var/www/__slpx2/www/mssql.danmarcrm.com/dev1/bMonV3_2/fetch_v2026/daily_digest.php >> .../logs/cron_digest.log 2>&1 ``` --- ## 7. Key File Paths | Path | Description | |------|-------------| | `fetch_v2026/daily_digest.php` | Daily AI digest main script | | `fetch_v2026/daily_digest_config.php` | Digest configuration (prompts, email, sections) | | `fetch_v2026/nightlyFetch.sh` | Nightly fetch orchestrator | | `fetch_v2026/jobScheduler_propsid.php` | Creates fetch jobs from propsid table | | `fetch_v2026/jobWorker.sh` | Individual worker (claims + processes jobs) | | `fetch_v2026/getHotelAndRoomDetailsPool.php` | Scraper using browser pool | | `fetch_v2026/dashboard.php` | Fetch monitoring dashboard | | `fetch_v2026/config.php` | Fetch system configuration | | `fetch_v2026/pool/` | Browser pool service (PM2) | | `fetch_v2026/logs/` | All log files | | `reportsV3/report_pricing_v1.php` | Pricing reports dashboard | | `mongodb/MongoConnection.php` | MongoDB wrapper class | --- ## 8. Test Results (2026-03-07 21:03) Full pipeline test (`--stdout`): - **Data**: 30 hotels, 8 checkin dates, 204 hotels in propsid, 30,814 char prompt - **AI model**: claude-sonnet-4-20250514 - **AI response**: 1,946 chars - **MongoDB**: Saved to `price_ai_analyses` - **Email**: Sent via SendGrid (HTTP 202) to dan@danmarcrm.com - **Note**: Zenith showed no data because last fetch was 2026-02-28 (outside 3-day window). After tonight's 2 AM fetch, the 8 AM digest will include fresh Zenith data. --- ## 9. Infrastructure Notes - **PHP**: 8.3 (`/usr/bin/php8.3`) - **MongoDB**: Local, database `booking_analysis`, port 27017 - **Browser Pool**: PM2-managed, 20 browsers, port 3100, local only - **Zenith Hotel IDs**: 13613044, 15081492, 15046719, 13121164 - **Server**: `/var/www/__slpx2/www/mssql.danmarcrm.com/dev1/bMonV3_2` - **URL**: `https://bmonv3-2.danmarcrm.com`
Hidden (hide from default list)
Access Methods
(web, android, ios, api, etc.)
Type
URL
Notes
Remove
+ Add Access Method
Code Locations
Label
Host
Path
SSH Command
SSH Port
SSH Key Path
Notes
Remove
+ Add Location
Databases
Type
Name
Host
Notes
Remove
+ Add Database
Links
Label
URL
Remove
+ Add Link
Save Changes
Cancel