feat: add Demographics & Analytics tool to Fusion Claims Intelligence
- Add Tool 6 for demographic analysis using direct SQL queries - Age group breakdowns: clients, applications, avg apps/client, avg funding - Device popularity by age bracket (under 45, 45-60, 61-75, 75+) - City demographics with average age and funding per city - Benefit type analysis (ODSP, OWP, ACSD, Regular) - Top devices with average client age - Overall funding summary (totals, averages, age range) - Update AI topic and system prompt with Tool 6 routing examples
This commit is contained in:
@@ -50,6 +50,20 @@ STATUS_NEXT_STEPS = {
|
||||
'expired': 'Contact client about reapplication if still needed',
|
||||
}
|
||||
|
||||
BASE_DEVICE_LABELS = {
|
||||
'adultWalkertype1': 'Adult Walker Type 1',
|
||||
'adultWalkertype2': 'Adult Walker Type 2',
|
||||
'adultWalkertype3': 'Adult Walker Type 3',
|
||||
'adultlightwtStdwheelchair': 'Lightweight Standard Wheelchair',
|
||||
'adultlightwtPermwheelchair': 'Lightweight Permanent Wheelchair',
|
||||
'adultTiltwheelchair': 'Adult Tilt Wheelchair',
|
||||
'adultStdwheelchair': 'Adult Standard Wheelchair',
|
||||
'adultHighperfwheelchair': 'Adult High Performance Wheelchair',
|
||||
'adultType2': 'Adult Power Type 2',
|
||||
'adultType3': 'Adult Power Type 3',
|
||||
'powerScooter': 'Power Scooter',
|
||||
}
|
||||
|
||||
|
||||
class AIAgentFusionClaims(models.Model):
|
||||
"""Extend ai.agent with Fusion Claims tool methods."""
|
||||
@@ -450,3 +464,207 @@ class AIAgentFusionClaims(models.Model):
|
||||
},
|
||||
'invoices': invoice_details,
|
||||
})
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Tool 6: Demographics & Analytics
|
||||
# ------------------------------------------------------------------
|
||||
def _fc_tool_demographics(self, analysis_type=None, city_filter=None, sale_type_filter=None):
|
||||
"""AI Tool: Run demographic and analytical queries on client data."""
|
||||
cr = self.env.cr
|
||||
results = {}
|
||||
|
||||
if not analysis_type:
|
||||
analysis_type = 'full'
|
||||
|
||||
if analysis_type in ('full', 'age_groups'):
|
||||
cr.execute("""
|
||||
SELECT
|
||||
CASE
|
||||
WHEN age < 18 THEN 'Under 18'
|
||||
WHEN age BETWEEN 18 AND 30 THEN '18-30'
|
||||
WHEN age BETWEEN 31 AND 45 THEN '31-45'
|
||||
WHEN age BETWEEN 46 AND 60 THEN '46-60'
|
||||
WHEN age BETWEEN 61 AND 75 THEN '61-75'
|
||||
ELSE '75+'
|
||||
END AS age_group,
|
||||
COUNT(DISTINCT p.id) AS clients,
|
||||
COUNT(app.id) AS applications,
|
||||
ROUND(COUNT(app.id)::numeric / NULLIF(COUNT(DISTINCT p.id), 0), 2) AS avg_applications,
|
||||
COALESCE(ROUND(SUM(p.total_adp_funded) / NULLIF(COUNT(DISTINCT p.id), 0), 2), 0) AS avg_adp_funded,
|
||||
COALESCE(ROUND(SUM(p.total_amount) / NULLIF(COUNT(DISTINCT p.id), 0), 2), 0) AS avg_total
|
||||
FROM fusion_client_profile p
|
||||
CROSS JOIN LATERAL (
|
||||
SELECT EXTRACT(YEAR FROM AGE(CURRENT_DATE, p.date_of_birth))::int AS age
|
||||
) a
|
||||
LEFT JOIN fusion_adp_application_data app ON app.profile_id = p.id
|
||||
WHERE p.date_of_birth IS NOT NULL
|
||||
GROUP BY age_group
|
||||
ORDER BY MIN(age)
|
||||
""")
|
||||
rows = cr.fetchall()
|
||||
results['age_groups'] = [
|
||||
{
|
||||
'age_group': r[0], 'clients': r[1], 'applications': r[2],
|
||||
'avg_applications': float(r[3]), 'avg_adp_funded': float(r[4]),
|
||||
'avg_total': float(r[5]),
|
||||
} for r in rows
|
||||
]
|
||||
|
||||
if analysis_type in ('full', 'devices_by_age'):
|
||||
cr.execute("""
|
||||
SELECT
|
||||
CASE
|
||||
WHEN age < 45 THEN 'Under 45'
|
||||
WHEN age BETWEEN 45 AND 60 THEN '45-60'
|
||||
WHEN age BETWEEN 61 AND 75 THEN '61-75'
|
||||
ELSE '75+'
|
||||
END AS age_group,
|
||||
app.base_device,
|
||||
COUNT(*) AS count
|
||||
FROM fusion_client_profile p
|
||||
CROSS JOIN LATERAL (
|
||||
SELECT EXTRACT(YEAR FROM AGE(CURRENT_DATE, p.date_of_birth))::int AS age
|
||||
) a
|
||||
JOIN fusion_adp_application_data app ON app.profile_id = p.id
|
||||
WHERE p.date_of_birth IS NOT NULL
|
||||
AND app.base_device IS NOT NULL AND app.base_device != ''
|
||||
GROUP BY age_group, app.base_device
|
||||
ORDER BY age_group, count DESC
|
||||
""")
|
||||
rows = cr.fetchall()
|
||||
devices_by_age = {}
|
||||
for age_group, device, count in rows:
|
||||
if age_group not in devices_by_age:
|
||||
devices_by_age[age_group] = []
|
||||
label = BASE_DEVICE_LABELS.get(device, device)
|
||||
devices_by_age[age_group].append({'device': label, 'count': count})
|
||||
results['devices_by_age'] = devices_by_age
|
||||
|
||||
if analysis_type in ('full', 'city_demographics'):
|
||||
city_clause = ""
|
||||
params = []
|
||||
if city_filter:
|
||||
city_clause = "AND LOWER(p.city) = LOWER(%s)"
|
||||
params = [city_filter]
|
||||
|
||||
cr.execute(f"""
|
||||
SELECT
|
||||
p.city,
|
||||
COUNT(DISTINCT p.id) AS clients,
|
||||
COUNT(app.id) AS applications,
|
||||
ROUND(AVG(a.age), 1) AS avg_age,
|
||||
COALESCE(ROUND(SUM(p.total_adp_funded) / NULLIF(COUNT(DISTINCT p.id), 0), 2), 0) AS avg_adp_funded
|
||||
FROM fusion_client_profile p
|
||||
CROSS JOIN LATERAL (
|
||||
SELECT EXTRACT(YEAR FROM AGE(CURRENT_DATE, p.date_of_birth))::int AS age
|
||||
) a
|
||||
LEFT JOIN fusion_adp_application_data app ON app.profile_id = p.id
|
||||
WHERE p.city IS NOT NULL AND p.city != ''
|
||||
AND p.date_of_birth IS NOT NULL
|
||||
{city_clause}
|
||||
GROUP BY p.city
|
||||
ORDER BY clients DESC
|
||||
LIMIT 15
|
||||
""", params)
|
||||
rows = cr.fetchall()
|
||||
results['city_demographics'] = [
|
||||
{
|
||||
'city': r[0], 'clients': r[1], 'applications': r[2],
|
||||
'avg_age': float(r[3]), 'avg_adp_funded': float(r[4]),
|
||||
} for r in rows
|
||||
]
|
||||
|
||||
if analysis_type in ('full', 'benefits'):
|
||||
cr.execute("""
|
||||
SELECT
|
||||
CASE
|
||||
WHEN p.benefit_type = 'odsp' THEN 'ODSP'
|
||||
WHEN p.benefit_type = 'owp' THEN 'Ontario Works'
|
||||
WHEN p.benefit_type = 'acsd' THEN 'ACSD'
|
||||
WHEN p.receives_social_assistance THEN 'Social Assistance (other)'
|
||||
ELSE 'Regular (no assistance)'
|
||||
END AS benefit_category,
|
||||
COUNT(DISTINCT p.id) AS clients,
|
||||
COUNT(app.id) AS applications,
|
||||
ROUND(AVG(a.age), 1) AS avg_age,
|
||||
COALESCE(ROUND(AVG(p.total_adp_funded), 2), 0) AS avg_adp_funded
|
||||
FROM fusion_client_profile p
|
||||
CROSS JOIN LATERAL (
|
||||
SELECT EXTRACT(YEAR FROM AGE(CURRENT_DATE, p.date_of_birth))::int AS age
|
||||
) a
|
||||
LEFT JOIN fusion_adp_application_data app ON app.profile_id = p.id
|
||||
WHERE p.date_of_birth IS NOT NULL
|
||||
GROUP BY benefit_category
|
||||
ORDER BY clients DESC
|
||||
""")
|
||||
rows = cr.fetchall()
|
||||
results['benefits_breakdown'] = [
|
||||
{
|
||||
'category': r[0], 'clients': r[1], 'applications': r[2],
|
||||
'avg_age': float(r[3]), 'avg_adp_funded': float(r[4]),
|
||||
} for r in rows
|
||||
]
|
||||
|
||||
if analysis_type in ('full', 'top_devices'):
|
||||
cr.execute("""
|
||||
SELECT
|
||||
app.base_device,
|
||||
COUNT(*) AS count,
|
||||
COUNT(DISTINCT app.profile_id) AS unique_clients,
|
||||
ROUND(AVG(a.age), 1) AS avg_age
|
||||
FROM fusion_adp_application_data app
|
||||
JOIN fusion_client_profile p ON p.id = app.profile_id
|
||||
CROSS JOIN LATERAL (
|
||||
SELECT EXTRACT(YEAR FROM AGE(CURRENT_DATE, p.date_of_birth))::int AS age
|
||||
) a
|
||||
WHERE app.base_device IS NOT NULL AND app.base_device != '' AND app.base_device != 'none'
|
||||
AND p.date_of_birth IS NOT NULL
|
||||
GROUP BY app.base_device
|
||||
ORDER BY count DESC
|
||||
LIMIT 15
|
||||
""")
|
||||
rows = cr.fetchall()
|
||||
results['top_devices'] = [
|
||||
{
|
||||
'device': BASE_DEVICE_LABELS.get(r[0], r[0]),
|
||||
'device_code': r[0],
|
||||
'applications': r[1],
|
||||
'unique_clients': r[2],
|
||||
'avg_client_age': float(r[3]),
|
||||
} for r in rows
|
||||
]
|
||||
|
||||
if analysis_type in ('full', 'funding_summary'):
|
||||
cr.execute("""
|
||||
SELECT
|
||||
COUNT(*) AS total_profiles,
|
||||
ROUND(AVG(a.age), 1) AS avg_age,
|
||||
ROUND(SUM(p.total_adp_funded), 2) AS total_adp_funded,
|
||||
ROUND(SUM(p.total_client_portion), 2) AS total_client_portion,
|
||||
ROUND(SUM(p.total_amount), 2) AS grand_total,
|
||||
ROUND(AVG(p.total_adp_funded), 2) AS avg_adp_per_client,
|
||||
ROUND(AVG(p.total_client_portion), 2) AS avg_client_per_client,
|
||||
ROUND(AVG(p.claim_count), 2) AS avg_claims_per_client,
|
||||
MIN(a.age) AS youngest,
|
||||
MAX(a.age) AS oldest
|
||||
FROM fusion_client_profile p
|
||||
CROSS JOIN LATERAL (
|
||||
SELECT EXTRACT(YEAR FROM AGE(CURRENT_DATE, p.date_of_birth))::int AS age
|
||||
) a
|
||||
WHERE p.date_of_birth IS NOT NULL
|
||||
""")
|
||||
r = cr.fetchone()
|
||||
results['funding_summary'] = {
|
||||
'total_profiles': r[0],
|
||||
'avg_age': float(r[1]),
|
||||
'total_adp_funded': float(r[2]),
|
||||
'total_client_portion': float(r[3]),
|
||||
'grand_total': float(r[4]),
|
||||
'avg_adp_per_client': float(r[5]),
|
||||
'avg_client_per_client': float(r[6]),
|
||||
'avg_claims_per_client': float(r[7]),
|
||||
'youngest_client_age': r[8],
|
||||
'oldest_client_age': r[9],
|
||||
}
|
||||
|
||||
return json.dumps(results)
|
||||
|
||||
Reference in New Issue
Block a user