mirror of
https://github.com/CompassConnections/Compass.git
synced 2026-03-24 17:41:27 -04:00
Add gender retio to stats
This commit is contained in:
@@ -1,22 +1,64 @@
|
||||
import {getMessagesCount} from 'api/get-messages-count'
|
||||
import {HOUR_MS} from 'common/util/time'
|
||||
import {createSupabaseDirectClient} from 'shared/supabase/init'
|
||||
|
||||
import {APIHandler} from './helpers/endpoint'
|
||||
|
||||
// Server-side cache for stats data
|
||||
let cachedData: any = null
|
||||
let cacheTimestamp: number = 0
|
||||
const CACHE_DURATION_MS = HOUR_MS
|
||||
|
||||
export const stats: APIHandler<'stats'> = async (_, _auth) => {
|
||||
const now = Date.now()
|
||||
|
||||
// Return cached data if still valid
|
||||
if (cachedData && now - cacheTimestamp < CACHE_DURATION_MS) {
|
||||
console.log('cached stats')
|
||||
console.log(cachedData)
|
||||
return cachedData
|
||||
}
|
||||
|
||||
const pg = createSupabaseDirectClient()
|
||||
|
||||
const [userCount, profileCount, eventsCount, messagesCount] = await Promise.all([
|
||||
const [userCount, profileCount, eventsCount, messagesCount, genderStats] = await Promise.all([
|
||||
pg.one(`SELECT COUNT(*)::int as count FROM users`),
|
||||
pg.one(`SELECT COUNT(*)::int as count FROM profiles`),
|
||||
pg.one(`SELECT COUNT(*)::int as count FROM events WHERE event_start_time > now()`),
|
||||
getMessagesCount(),
|
||||
pg.manyOrNone(
|
||||
`SELECT gender, COUNT(*)::int as count FROM profiles WHERE gender IS NOT NULL GROUP BY gender`,
|
||||
),
|
||||
])
|
||||
|
||||
return {
|
||||
// Calculate gender ratios
|
||||
const genderRatio: Record<string, number> = {}
|
||||
let totalWithGender = 0
|
||||
|
||||
genderStats?.forEach((stat: any) => {
|
||||
if (!['male', 'female'].includes(stat.gender)) return
|
||||
genderRatio[stat.gender] = stat.count
|
||||
totalWithGender += stat.count
|
||||
})
|
||||
|
||||
// Convert to percentages
|
||||
const genderPercentage: Record<string, number> = {}
|
||||
Object.entries(genderRatio).forEach(([gender, count]) => {
|
||||
genderPercentage[gender] = Math.round((count / totalWithGender) * 100)
|
||||
})
|
||||
|
||||
const result = {
|
||||
users: userCount.count,
|
||||
profiles: profileCount.count,
|
||||
upcomingEvents: eventsCount.count,
|
||||
messages: messagesCount.count,
|
||||
genderRatio: genderPercentage,
|
||||
genderCounts: genderRatio,
|
||||
}
|
||||
|
||||
// Update cache
|
||||
cachedData = result
|
||||
cacheTimestamp = now
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -154,6 +154,9 @@ export const API = (_apiTypeCheck = {
|
||||
users: number
|
||||
profiles: number
|
||||
upcomingEvents: number
|
||||
messages: number
|
||||
genderRatio: Record<string, number>
|
||||
genderCounts: Record<string, number>
|
||||
},
|
||||
summary: 'Get platform statistics',
|
||||
tag: 'General',
|
||||
|
||||
@@ -5,12 +5,14 @@ import {PageBase} from 'web/components/page-base'
|
||||
import {SEO} from 'web/components/SEO'
|
||||
import ChartMembers from 'web/components/widgets/charts'
|
||||
import StatBox from 'web/components/widgets/stat-box'
|
||||
import {api} from 'web/lib/api'
|
||||
import {useT} from 'web/lib/locale'
|
||||
import {getCount} from 'web/lib/supabase/users'
|
||||
|
||||
export default function Stats() {
|
||||
const t = useT()
|
||||
const [data, setData] = useState<Record<string, number | null>>({})
|
||||
const [statsData, setStatsData] = useState<any>(null)
|
||||
|
||||
useEffect(() => {
|
||||
async function load() {
|
||||
@@ -27,14 +29,23 @@ export default function Stats() {
|
||||
'vote_results',
|
||||
] as const
|
||||
|
||||
const settled = await Promise.allSettled(tables.map((t) => getCount(t)))
|
||||
const [settled, statsResult] = await Promise.allSettled([
|
||||
Promise.allSettled(tables.map((t) => getCount(t))),
|
||||
api('stats', {}),
|
||||
])
|
||||
|
||||
const result: Record<string, number | null> = {}
|
||||
settled.forEach((res, i) => {
|
||||
const key = tables[i]
|
||||
if (res.status === 'fulfilled') result[key] = res.value
|
||||
else result[key] = null
|
||||
})
|
||||
if (settled.status === 'fulfilled') {
|
||||
settled.value.forEach((res, i) => {
|
||||
const key = tables[i]
|
||||
if (res.status === 'fulfilled') result[key] = res.value
|
||||
else result[key] = null
|
||||
})
|
||||
}
|
||||
|
||||
if (statsResult.status === 'fulfilled') {
|
||||
setStatsData(statsResult.value)
|
||||
}
|
||||
|
||||
setData(result)
|
||||
}
|
||||
@@ -108,6 +119,12 @@ export default function Stats() {
|
||||
label={t('stats.endorsements', 'Endorsements')}
|
||||
/>
|
||||
)}
|
||||
{!!statsData?.genderRatio && (
|
||||
<StatBox
|
||||
value={`${statsData.genderRatio.male} / ${statsData.genderRatio.female}`}
|
||||
label={t('stats.gender_ratio', 'Male / Female Ratio')}
|
||||
/>
|
||||
)}
|
||||
</Col>
|
||||
</Col>
|
||||
</PageBase>
|
||||
|
||||
Reference in New Issue
Block a user