Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(metrics): add mutation to generate usage report #10236

Merged
merged 11 commits into from
Sep 24, 2024
127 changes: 127 additions & 0 deletions packages/server/graphql/private/mutations/runOrgActivityReport.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import {sql} from 'kysely'
import getRethink from '../../../database/rethinkDriver'
import {RDatum, RValue} from '../../../database/stricterR'
import getKysely from '../../../postgres/getKysely'
import {MutationResolvers} from '../resolverTypes'

const runOrgActivityReport: MutationResolvers['runOrgActivityReport'] = async (
_source,
{startDate, endDate}
) => {
const pg = getKysely()
const now = new Date()
const queryEndDate = endDate || now
const queryStartDate = startDate || new Date(0) // Unix epoch start if not provided

const months = pg
.selectFrom(
sql`generate_series(
date_trunc('month', ${queryStartDate.toISOString()}::date),
date_trunc('month', ${queryEndDate.toISOString()}::date),
${sql`'1 month'::interval`}
)`.as('monthStart')
)
.selectAll()

const userSignups = pg
.selectFrom('User')
.select([
sql`date_trunc('month', "createdAt")`.as('month'),
sql`COUNT(DISTINCT "id")`.as('signup_count')
])
.where('createdAt', '>=', queryStartDate)
.where('createdAt', '<', queryEndDate)
.groupBy(sql`date_trunc('month', "createdAt")`)

const userLogins = pg
.selectFrom('User')
.select([
sql`date_trunc('month', "lastSeenAt")`.as('month'),
sql`COUNT(DISTINCT "id")`.as('login_count')
])
.where('lastSeenAt', '>=', queryStartDate)
.where('lastSeenAt', '<', queryEndDate)
.groupBy(sql`date_trunc('month', "lastSeenAt")`)

const query = pg
.selectFrom(months.as('m'))
tianrunhe marked this conversation as resolved.
Show resolved Hide resolved
.leftJoin(userSignups.as('us'), (join) =>
join.onRef(sql`m."monthStart"`, '=', sql`us.month::timestamp`)
)
.leftJoin(userLogins.as('ul'), (join) =>
join.onRef(sql`m."monthStart"`, '=', sql`ul.month::timestamp`)
)
.select([
sql`m."monthStart"`.as('monthStart'),
sql`(m."monthStart" + interval '1 month' - interval '1 day')::date`.as('lastDayOfMonth'),
sql`'All Organizations'`.as('orgName'),
sql`COALESCE(us.signup_count, 0)`.as('signupCount'),
sql`COALESCE(ul.login_count, 0)`.as('loginCount')
])
.orderBy('monthStart')

const r = await getRethink()
try {
const [pgResults, rethinkResults] = await Promise.all([
query.execute(),
r
.table('NewMeeting')
.between(
r.epochTime(queryStartDate.getTime() / 1000),
r.epochTime(queryEndDate.getTime() / 1000),
{index: 'createdAt'}
)
.merge((row: RValue) => ({
yearMonth: {
year: row('createdAt').year(),
month: row('createdAt').month()
}
}))
.group((row) => row('yearMonth'))
.ungroup()
.map((group: RDatum) => ({
yearMonth: group('group'),
meetingCount: group('reduction').count(),
participantIds: group('reduction')
.concatMap((row: RDatum) =>
r.table('MeetingMember').getAll(row('id'), {index: 'meetingId'})('userId')
)
.distinct()
}))
.map((row: RDatum) =>
row.merge({
participantCount: row('participantIds').count()
})
)
.without('participantIds')
.run()
])

// Combine PostgreSQL and RethinkDB results
const combinedResults = pgResults.map((pgRow: any) => {
const monthStart = new Date(pgRow.monthStart)
const rethinkParticipants = rethinkResults.find(
(r: any) =>
r.yearMonth.month === monthStart.getUTCMonth() + 1 &&
r.yearMonth.year === monthStart.getUTCFullYear()
)
const rethinkMeetings = rethinkResults.find(
(r: any) =>
r.yearMonth.month === monthStart.getUTCMonth() + 1 &&
r.yearMonth.year === monthStart.getUTCFullYear()
)

return {
...pgRow,
participantCount: rethinkParticipants ? rethinkParticipants.participantCount : 0,
meetingCount: rethinkMeetings ? rethinkMeetings.meetingCount : 0
}
})
return {rows: combinedResults}
} catch (error) {
console.error('Error executing Org Activity Report:', error)
return {error: {message: 'Error executing Org Activity Report'}}
}
}

export default runOrgActivityReport
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
extend type Mutation {
"""
Run org activity report
tianrunhe marked this conversation as resolved.
Show resolved Hide resolved
"""
runOrgActivityReport(
"""
the start date for the query
"""
startDate: DateTime

"""
the end date for the query
"""
endDate: DateTime
): RunOrgActivityReportPayload!
}

"""
Return value for runOrgActivityReport, which could be an error
"""
union RunOrgActivityReportPayload = ErrorPayload | RunOrgActivityReportSuccess

type RunOrgActivityReportSuccess {
"""
The rows of the report
"""
rows: [OrgActivityRow!]!
}

type OrgActivityRow {
"""
The start date of the month
"""
monthStart: DateTime!

"""
The last day of the month
"""
lastDayOfMonth: DateTime!

"""
The name of the organization
"""
orgName: String!
tianrunhe marked this conversation as resolved.
Show resolved Hide resolved

"""
The number of signups in the month
"""
signupCount: Int!

"""
The number of logins in the month
"""
loginCount: Int!

"""
The number of unique meeting participants in the month
"""
participantCount: Int!

"""
The number of meetings run in the month
"""
meetingCount: Int!
}
Loading