Mental health expansion: Difference between revisions

From Kautepedia
Jump to navigation Jump to search
Line 518: Line 518:
And then you can group the filtered clients by ethnicity:
And then you can group the filtered clients by ethnicity:


<syntaxhighlight>
<syntaxhighlight lang="postgresql" line>
WITH base AS (
WITH base AS (
     SELECT unnest(ARRAY[/*client id's here */])::int AS client_id
     SELECT unnest(ARRAY[/*client id's here */])::int AS client_id
Line 548: Line 548:


And by age:
And by age:
<syntaxhighlight>
<syntaxhighlight lang="postgresql" line>
 
 
WITH params AS (
    SELECT DATE '2025-07-01' AS rpt_start
),
base AS (
    SELECT unnest(ARRAY[2848,2837,2838,2836])::int AS client_id
),
ages AS (
    SELECT
        c."ClientID",
        CASE
            WHEN c."DOB" IS NULL THEN 'Unknown'
            WHEN FLOOR(EXTRACT(YEAR FROM age(p.rpt_start, c."DOB"))) BETWEEN 0  AND 11 THEN '00–11'
            WHEN FLOOR(EXTRACT(YEAR FROM age(p.rpt_start, c."DOB"))) BETWEEN 12 AND 17 THEN '12–17'
            WHEN FLOOR(EXTRACT(YEAR FROM age(p.rpt_start, c."DOB"))) BETWEEN 18 AND 24 THEN '18–24'
            WHEN FLOOR(EXTRACT(YEAR FROM age(p.rpt_start, c."DOB"))) BETWEEN 25 AND 64 THEN '25–64'
            WHEN FLOOR(EXTRACT(YEAR FROM age(p.rpt_start, c."DOB"))) >= 65          THEN '65+'
            ELSE 'Unknown'
        END AS age_group
    FROM base b
    JOIN kotahi.clients c ON c."ClientID" = b.client_id
    CROSS JOIN params p
)
SELECT
    age_group,
    COUNT(*) AS people_count
FROM ages
GROUP BY age_group
ORDER BY
    CASE age_group
        WHEN '00–11' THEN 1
        WHEN '12–17' THEN 2
        WHEN '18–24' THEN 3
        WHEN '25–64' THEN 4
        WHEN '65+'  THEN 5
        ELSE 6
    END;


</syntaxhighlight>
</syntaxhighlight>


==References==
==References==

Revision as of 01:30, 25 August 2025

Background

Mental Health Expansion is a contract/service provided by KPT.

Because we have a multidisciplinary Mental Health team working across a range of contracts, we tend to separate this service out in reporting by staff member and demographic of client group.[1]

At the current time, contact data for this service is split between two systems:

  • Kotahi – Community Day Programme (CDP) data is now recorded here.
  • Aiga – All other contact data for this service is still recorded here.

Reporting data must therefore be sourced from both Kotahi and Aiga.

Reporting

Funder reporting is required monthly, on the 20th of each month. Not all items are possible to obtain from the databases, but a script is being used for those that are available. This is specified below.

Data points

Per the reporting template, the following is a summary of items required alongside a summary of whether or not we can obtain them from Kotahi and/or Aiga data.

Item Available
FTE
Number of sessions
Number of people seen
Number of new people seen
Number of people waiting more than five days
Number of service exits
Number of rejected referrals
Number of onward referrals
Number of sessions involve whanau
Number of new people by ethnicity
Number of new people by age
Flexifund usage

Getting the data

At the present time data must be run directly from both the Aiga and Kotahi database. This would probably benefit from automating into Power BI at some point, but the impending Integrated contract means that the cost may not outweigh benefit in the short term.

Current draft SQL for Child and Youth MH contract is provided below.[2]

Aiga data

Base data for report month

-- get base data into temp table
select
ContractID,
ContractName,
ClientID,
EpisodeContractID,
NoteDate,
ContactTypeID,
ContactTypeActive,
case
	-- find contact types that are 'direct contacts'
	when ContactTypeID in (
	  2,
      3,
      4,
      13,
      15,
      19,
      20,
      24,
      27,
      34,
      35,
      37,
      38,
      39,
      41,
      63,
      68,
      69,
      72,
      75,
      76,
      77,
      83,
      85,
      86,
      88,
      89,
      90,
      93,
      94,
      97,
      98) then 1
      else 0
      end as 'Direct contact'

into #mhbase
from vClientNotesDocuments
where 
ContractID = 11 -- MHE contract ID
and datepart(mm,NoteDate) = 1 --> ## SET REPORT MONTH HERE
and datepart(yy,NoteDate) = 2025 --> ## SET REPORT YEAR HERE
and UserID <> 1111 -- exclude this user
;

New starts

-- new starts block
declare @MonthEndDate as date
declare @PriorYear as date

set @MonthEndDate = '2025-01-31' --> ## SET LAST DAY OF REPORT MONTH HERE
set @PriorYear = dateadd(mm,-11,@MonthEndDate)

print @MonthEndDate
print @PriorYear;

with ctea as
(
	-- find new starts in report month
	select 
	eco.ClientID,
	eco.EpisodeContractStartOrder,
	ec.*
	from vEpisodeContracts ec
	join vClientEpisodeContractOrder eco
	on ec.EpisodeContractID = eco.EpisodeContractID

	where
		ec.ContractID in (13,11) -- MHE and CDP contract IDs
		and 
		datepart(mm,ec.EpisodeContractStartDate) = datepart(mm,@MonthEndDate)
		and
		datepart(yy,ec.EpisodeContractStartDate) = datepart(yy,@MonthEndDate)
		and
		ec.EpisodeContractID in (select distinct EpisodeContractID from #mhbase)
)
,
cteb as
(
	-- check clientids from above have not had previous contract episode in last year
	select eco.*
	from vClientEpisodeContractOrder eco
	join ctea c
	on eco.EpisodeID = c.EpisodeID
	and
	eco.ContractID = c.ContractID
	where 
		eco.ContractID in (13,11)
		and
		eco.EpisodeContractStartOrder < c.EpisodeContractStartOrder
		and
		eco.EpisodeContractStartDate > @PriorYear
)

-- write summary data to temp table
select 
a.clientid,
a.contractname,
case
	when c.AgeInt between 0 and 11 then '0-11'
	when c.AgeInt between 12 and 17 then '12-17'
	when c.AgeInt between 18 and 24 then '18-24'
	when c.AgeInt between 25 and 64 then '25-64'
	when c.AgeInt > 64 then '65+'
	end as AgeGroup,
case
	when c.EthnicityID in (2) then 'Maori'
	when c.EthnicityID in (1) then 'NZ European'
	when c.EthnicityID not in (1,2,99,98) then 'Pasifika'
	when c.EthnicityID in (98,99) then 'Other'
	end as EthnicityGroup

into #mhnewpts
from ctea a
left join cteb b
on a.ClientID = b.ClientID
join vClients c
on a.ClientID = c.ClientID
where b.ClientID is null;

People seen

select
ContractName,
count(distinct clientid)

from #mhbase

where [direct contact] = 1
group by ContractName;

Service exits

select
ContractName,
count(*)
from vEpisodeContracts
where 
ContractID in (13,11)
and
	(datepart(yy,episodecontractenddate) = 2025 and 
	datepart(mm,episodecontractenddate) = 1)
and UserID <> 1111

group by ContractName
order by 1;

Individual sessions

select 
ContractName,
sum(case when [direct contact] = 1 and contacttypeid not in (19) then 1 else 0 end) IndividualSessions,
sum(case when contacttypeid = 19 and ContractName = 'Child and Adolescent Mental Health ' then 1 else 0 end) GroupSessions -- exclude calculating this for CDP, see next block

from #mhbase

group by ContractName;

Group sessions (CDP only)

with ctea as
-- group sessions/attendees for CDP
(
select
distinct cast(NoteDate as date) sessiondate,
count(*) attendees

from #mhbase

where ContractID = 13
and ContactTypeID = 19

group by NoteDate
)

select
case
	when attendees between 2 and 14 then '02-14'
	when attendees > 14 then '15+'
end as sessions,
count(*)

from ctea

group by
case
	when attendees between 2 and 14 then '02-14'
	when attendees > 14 then '15+'
end
;

New people by ethnicity

select 
ContractName,
EthnicityGroup,
count(*)

from #mhnewpts
group by 
ContractName,
EthnicityGroup

order by 1,2;

New people by age group

select 
ContractName,
AgeGroup,
count(*)

from #mhnewpts
group by 
ContractName,
AgeGroup

order by 1,2;

People waiting >5 days for first contact

select
  ContractID,
  ContractName,
  ClientID,
  EpisodeContractID,
  NoteDate,
  ContactTypeID,
  ContactTypeActive,
  case
    when ContactTypeID in (
      2, 3, 4, 13, 15, 19, 20, 24, 27, 34, 35, 37, 38, 39,
      41, 63, 68, 69, 72, 75, 76, 77, 83, 85, 86, 88, 89,
      90, 93, 94, 97, 98
    ) then 1
    else 0
  end as [Direct contact]
into #mhbase
from vClientNotesDocuments
where 
  ContractID in (13,11)
  and datepart(mm, NoteDate) = 6
  and datepart(yy, NoteDate) = 2025
  and UserID <> 1111;

select 
  eco.EpisodeContractID,
  ec.ContractName,
  ec.EpisodeContractStartDate
into #mhnewec
from vEpisodeContracts ec
join vClientEpisodeContractOrder eco
  on ec.EpisodeContractID = eco.EpisodeContractID
where ec.ContractID in (13,11)
  and datepart(mm, ec.EpisodeContractStartDate) = 6
  and datepart(yy, ec.EpisodeContractStartDate) = 2025
  and ec.EpisodeContractID in (select distinct EpisodeContractID from #mhbase);


-- Get earliest direct contact for each EpisodeContract
with ctea as (
    select
        cnd.EpisodeContractID,
        min(cnd.NoteDate) as firstnote
    from vClientNotesDocuments cnd
    where 
        cnd.EpisodeContractID in (select EpisodeContractID from #mhnewec)
        and ContactTypeID in (
            2, 3, 4, 13, 15, 19, 20, 24, 27, 34,
            35, 37, 38, 39, 41, 63, 68, 69, 72,
            75, 76, 77, 83, 85, 86, 88, 89, 90,
            93, 94, 97, 98
        )
    group by cnd.EpisodeContractID
)

-- Count people who waited >5 days for first contact, only if there's enough time in the month
select
    ec.ContractName,
    count(*) as ClientsWaitingOver5Days
from #mhnewec ec
left join ctea c on ec.EpisodeContractID = c.EpisodeContractID
where 
    datediff(day, ec.EpisodeContractStartDate, c.firstnote) > 5
    and datediff(day, ec.EpisodeContractStartDate, eomonth(ec.EpisodeContractStartDate)) > 5
group by ec.ContractName
;

Kotahi data ( CDP)

People seen

WITH params AS (
  SELECT 
--SET REPORTING PERIOD HERE
    DATE '2025-07-01' AS start_date, 
    DATE '2025-07-31' AS end_date
),
group_participants AS (
  SELECT DISTINCT
    COALESCE(
      CAST("ClientID" AS TEXT),
      'other_' || CAST(participant_id AS TEXT)
    ) AS participant_key
  FROM kotahi.vw_groupactivity_clients, params
  WHERE "Attendance" = 1
    AND groupactivity_id IN (
      SELECT "Id"
      FROM kotahi.groupactivities, params
      WHERE "ServiceId" = 6
        AND "Date" BETWEEN params.start_date AND params.end_date
    )
),
one_on_one AS (
  SELECT DISTINCT
    CAST("ClientId" AS TEXT) AS participant_key
  FROM kotahi.activities a, params
  WHERE a."ActivityTypeId" IN (60, 61, 62, 63, 64, 65, 86)
    AND a."ActivityStart" BETWEEN params.start_date AND params.end_date
    AND lower(a."CustomFieldDataJson" ->> 'ClientPresent') = 'yes'
    AND "ClientId" IS NOT NULL
)
SELECT COUNT(DISTINCT participant_key) AS people_seen
FROM (
  SELECT participant_key FROM group_participants
  UNION
  SELECT participant_key FROM one_on_one
) combined;

Service exits

SELECT 
  s."Name" AS service_name,
  COUNT(*) AS exit_count
FROM kotahi.activities a
JOIN kotahi.activitytypes at ON a."ActivityTypeId" = at."Id"
JOIN kotahi.services s ON at."ServiceId" = s."Id"
WHERE a."ActivityTypeId" = 86  -- activity indicating exit
  AND a."ActivityStart" BETWEEN DATE '2025-01-01' AND DATE '2025-01-31' --SET REPORTING PERIOD HERE
GROUP BY s."Name"
ORDER BY s."Name";

Individual sessions

SELECT COUNT(*) AS individual_sessions
FROM kotahi.activities a
WHERE a."ActivityTypeId" = 62  -- Narrative Case Note
  AND a."ActivityStart" BETWEEN DATE '2025-07-01' AND DATE '2025-07-31'
  AND lower(a."CustomFieldDataJson" ->> 'ContactType') <> 'group session'
  AND lower(a."CustomFieldDataJson" ->> 'ContactType') <> 'did not attend'
  AND lower(a."CustomFieldDataJson" ->> 'ContactType') <> 'administration'
  AND lower(a."CustomFieldDataJson" ->> 'ContactMethod') <> 'email'

group sessions

 WITH params AS (
  -- Set the first day of the reporting month here
  SELECT DATE '2025-06-01' AS rpt_start
),
session_attendance AS (
  SELECT
    ga."Id" AS group_id,
    ga."Date" AS session_date,
    COUNT(DISTINCT gcp."ClientParticipantId") AS attendees
  FROM kotahi.groupactivities ga
  JOIN kotahi.groupactivityclientparticipant gcp
    ON ga."Id" = gcp."GroupActivityId"
  CROSS JOIN params p
  WHERE ga."ServiceId" = 6
    AND ga."Date" >= p.rpt_start
    AND ga."Date" <  (p.rpt_start + INTERVAL '1 month')
    AND gcp."Attendance" = 1
  GROUP BY ga."Id", ga."Date"
)
SELECT
  CASE
    WHEN attendees BETWEEN 2 AND 14 THEN '02–14'
    WHEN attendees >= 15 THEN '15+'
    ELSE 'Fewer than 2 (ignored)'
  END AS session_size_band,
  COUNT(*) AS session_count
FROM session_attendance
WHERE attendees >= 2         -- exclude 1-person sessions which I doubt will ever happen 
GROUP BY session_size_band
ORDER BY session_size_band;

New people seen

This relies on aiga data being migrated! - So in the mean time this list needs to be filtered manually by cross referencing Aiga :'(

WITH params AS (
  SELECT DATE '2025-08-01' AS rpt_start  -- Set reporting period start
),
enrolled_in_month AS (
  SELECT
    a."ClientId",
    MIN(a."ActivityStart") AS enrolment_date   -- first enrolment form in the month
  FROM kotahi.activities a
  CROSS JOIN params p
  WHERE a."ActivityTypeId" = 60                -- enrolment form
    AND a."ClientId" IS NOT NULL
    AND a."ActivityStart" >= p.rpt_start
    AND a."ActivityStart" <  (p.rpt_start + INTERVAL '1 month')
  GROUP BY a."ClientId"
)
select
  c."ClientID" ,
  c."FirstName",
  c."LastName",
  e.enrolment_date AS "ActivityStart"
FROM enrolled_in_month e
JOIN kotahi.clients c
  ON c."ClientID" = e."ClientId"
CROSS JOIN params p
-- Exclude clients with any CDP (ServiceId = 6) activity in the prior 11 months
WHERE NOT EXISTS (
  SELECT 1
  FROM kotahi.activities a2
  JOIN kotahi.activitytypes aty2
    ON aty2."Id" = a2."ActivityTypeId"
  WHERE a2."ClientId"   = e."ClientId"
    AND aty2."ServiceId" = 6
    AND a2."ActivityStart" >= (p.rpt_start - INTERVAL '11 months')
    AND a2."ActivityStart" <  p.rpt_start
)
ORDER BY c."LastName", c."FirstName", e.enrolment_date;

And then you can group the filtered clients by ethnicity:

WITH base AS (
    SELECT unnest(ARRAY[/*client id's here */])::int AS client_id
),
eth AS (
    SELECT 
        c."ClientID",
        e."Description" AS ethnicity
    FROM base b
    JOIN kotahi.clients c ON c."ClientID" = b.client_id
    LEFT JOIN kotahi.ethnicities e 
           ON e."ID" = c."PrioritisedEthnicity"
)
SELECT CASE
           WHEN ethnicity ILIKE 'Māori' THEN 'Māori'
           WHEN ethnicity ILIKE ANY (ARRAY[
                'Samoan','Tongan','Cook Island','Niuean','Tokelauan',
                'Fijian','Other Pacific Island','Pasifika'
           ]) THEN 'Pasifika'
           WHEN ethnicity ILIKE ANY (ARRAY['Chinese','Indian','Asian%']) THEN 'Asian'
           WHEN ethnicity ILIKE 'NZ European' OR ethnicity ILIKE 'New Zealand European' THEN 'NZ European'
           ELSE 'Other'
       END AS ethnicity_group,
       COUNT(*) AS people_count
FROM eth
GROUP BY ethnicity_group
ORDER BY ethnicity_group;

And by age:

WITH params AS (
    SELECT DATE '2025-07-01' AS rpt_start
),
base AS (
    SELECT unnest(ARRAY[2848,2837,2838,2836])::int AS client_id
),
ages AS (
    SELECT
        c."ClientID",
        CASE
            WHEN c."DOB" IS NULL THEN 'Unknown'
            WHEN FLOOR(EXTRACT(YEAR FROM age(p.rpt_start, c."DOB"))) BETWEEN 0  AND 11 THEN '00–11'
            WHEN FLOOR(EXTRACT(YEAR FROM age(p.rpt_start, c."DOB"))) BETWEEN 12 AND 17 THEN '12–17'
            WHEN FLOOR(EXTRACT(YEAR FROM age(p.rpt_start, c."DOB"))) BETWEEN 18 AND 24 THEN '18–24'
            WHEN FLOOR(EXTRACT(YEAR FROM age(p.rpt_start, c."DOB"))) BETWEEN 25 AND 64 THEN '25–64'
            WHEN FLOOR(EXTRACT(YEAR FROM age(p.rpt_start, c."DOB"))) >= 65          THEN '65+'
            ELSE 'Unknown'
        END AS age_group
    FROM base b
    JOIN kotahi.clients c ON c."ClientID" = b.client_id
    CROSS JOIN params p
)
SELECT
    age_group,
    COUNT(*) AS people_count
FROM ages
GROUP BY age_group
ORDER BY
    CASE age_group
        WHEN '00–11' THEN 1
        WHEN '12–17' THEN 2
        WHEN '18–24' THEN 3
        WHEN '25–64' THEN 4
        WHEN '65+'   THEN 5
        ELSE 6
    END;

References

  1. Note that mental health contracts have seen a large number of modifications and revisions, and this service is also referred to as 'Access and Choice'.
  2. Any identifiers or sensitive information are redacted.