The TutorBot uses a sophisticated planning profile system to customize lesson scheduling based on customer segments. This system automatically detects customer segments and applies appropriate scheduling rules.
detect_segment() - modules/utils/mapping.py:283)The system automatically detects customer segments based on contact attributes:
def detect_segment(contact_id):
"""Detect segment based on contact attributes and history"""
from modules.utils.text_helpers import get_contact_attrs, set_contact_attrs
contact_attrs = get_contact_attrs(contact_id)
# Check if segment is already set (caching)
existing_segment = contact_attrs.get("segment")
if existing_segment:
return existing_segment
# 1. Weekend segment (whitelist check)
if contact_attrs.get("weekend_whitelisted"):
segment = "weekend"
# 2. Returning broadcast (begin school year list)
elif contact_attrs.get("returning_broadcast"):
segment = "returning_broadcast"
# 3. Existing customer - check multiple indicators
elif (contact_attrs.get("customer_since") or
contact_attrs.get("has_paid_lesson") or
contact_attrs.get("has_completed_intake") or
contact_attrs.get("intake_completed") or
contact_attrs.get("trial_lesson_completed") or
contact_attrs.get("lesson_booked") or
contact_attrs.get("customer_status") == "active"):
segment = "existing"
# 4. Default to new
else:
segment = "new"
# Cache segment for future calls
set_contact_attrs(contact_id, {"segment": segment})
return segment
| Segment | Description | Priority |
|---|---|---|
weekend |
Weekend whitelist customers | 1 (highest) |
returning_broadcast |
Returning from school year broadcast | 2 |
existing |
Existing customers with history | 3 |
new |
New customers (default) | 4 (lowest) |
PLANNING_PROFILES - modules/core/config.py:189)Each segment has a corresponding planning profile that defines scheduling rules:
# Planning profiles are dynamically generated based on environment variables
PLANNING_PROFILES = _get_planning_profiles()
def _get_planning_profiles():
"""Get planning profiles with test defaults when environment variables are not set"""
# Development/testing defaults
if not os.getenv("PLANNING_NEW_DURATION"):
return {
"new": { "duration_minutes": 60, "earliest_hour": 10, ... },
"existing": { "duration_minutes": 60, "earliest_hour": 9, ... },
"returning_broadcast": { "duration_minutes": 60, ... },
"weekend": { "duration_minutes": 90, "allowed_weekdays": [5, 6], ... },
"premium": { "duration_minutes": 90, "earliest_hour": 8, ... }
}
else:
# Production values from environment variables
return { ... }
| Parameter | Description | Example |
|---|---|---|
duration_minutes |
Lesson duration | 60, 90 |
earliest_hour |
Earliest start time | 8, 9, 10 |
latest_hour |
Latest start time | 18, 20, 21, 22 |
min_lead_minutes |
Minimum notice time | 180, 240, 360, 720 |
buffer_before_min |
Buffer before lesson | 10, 15, 20 |
buffer_after_min |
Buffer after lesson | 10, 15, 20 |
days_ahead |
Days to look ahead | 7, 10, 14, 21 |
slot_step_minutes |
Time slot intervals | 30 |
exclude_weekends |
Skip weekends | True, False |
allowed_weekdays |
Specific weekdays | [5, 6] for weekends |
"new": {
"duration_minutes": 60,
"earliest_hour": 10,
"latest_hour": 20,
"min_lead_minutes": 720, # 12 hours notice
"buffer_before_min": 15,
"buffer_after_min": 15,
"days_ahead": 10,
"slot_step_minutes": 30,
"exclude_weekends": True
}
"existing": {
"duration_minutes": 60,
"earliest_hour": 9,
"latest_hour": 21,
"min_lead_minutes": 360, # 6 hours notice
"buffer_before_min": 10,
"buffer_after_min": 10,
"days_ahead": 14,
"slot_step_minutes": 30,
"exclude_weekends": True
}
"weekend": {
"duration_minutes": 60,
"earliest_hour": 10,
"latest_hour": 18,
"min_lead_minutes": 180, # 3 hours notice
"buffer_before_min": 10,
"buffer_after_min": 10,
"days_ahead": 7,
"slot_step_minutes": 30,
"exclude_weekends": False,
"allowed_weekdays": [5, 6] # Saturday, Sunday
}
"premium": {
"duration_minutes": 90, # Longer lessons
"earliest_hour": 8,
"latest_hour": 22,
"min_lead_minutes": 240, # 4 hours notice
"buffer_before_min": 20,
"buffer_after_min": 20,
"days_ahead": 21, # 3 weeks ahead
"slot_step_minutes": 30,
"exclude_weekends": False # Includes weekends
}
suggest_slots() - modules/integrations/calendar_integration.py:16)# For "new" customer profile
profile = PLANNING_PROFILES["new"]
# Generates slots:
# - 10:00-11:00, 10:30-11:30, 11:00-12:00, etc.
# - Weekdays only (Mon-Fri)
# - 12+ hours notice required
# - Next 10 days only
The system respects user preferences stored in conversation attributes:
preferred_times = conv_attrs.get("preferred_times", "").lower()
Supported Preferences:
woensdag, donderdag, vrijdag, zaterdag, zondagochtend, middag, avond# Day matching
if "woensdag" in preferred_times and day_name != "wednesday":
continue
# Time matching
if "middag" in preferred_times and start_time.hour < 12:
continue
# Timezone configuration
TZ = ZoneInfo("Europe/Amsterdam")
# Calendar configuration
GCAL_CALENDAR_ID = "primary"
Profiles can be customized by modifying the PLANNING_PROFILES dictionary:
# Add new profile
PLANNING_PROFILES["custom"] = {
"duration_minutes": 45,
"earliest_hour": 9,
"latest_hour": 17,
# ... other parameters
}
Last Updated: December 2024
Status: Fully Implemented and Operational