Skip to content

Commit

Permalink
✨ (setVariable) Add Transcription system var (#1507)
Browse files Browse the repository at this point in the history
Closes #1484
  • Loading branch information
baptisteArno authored May 15, 2024
1 parent ec7ff8d commit 40f2120
Show file tree
Hide file tree
Showing 102 changed files with 2,911 additions and 986 deletions.
137 changes: 137 additions & 0 deletions apps/builder/src/features/analytics/api/getInDepthAnalyticsData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import prisma from '@typebot.io/lib/prisma'
import { authenticatedProcedure } from '@/helpers/server/trpc'
import { TRPCError } from '@trpc/server'
import { z } from 'zod'
import { canReadTypebots } from '@/helpers/databaseRules'
import { totalAnswersSchema } from '@typebot.io/schemas/features/analytics'
import { parseGroups } from '@typebot.io/schemas'
import { isInputBlock } from '@typebot.io/schemas/helpers'
import { defaultTimeFilter, timeFilterValues } from '../constants'
import {
parseFromDateFromTimeFilter,
parseToDateFromTimeFilter,
} from '../helpers/parseDateFromTimeFilter'

export const getInDepthAnalyticsData = authenticatedProcedure
.meta({
openapi: {
method: 'GET',
path: '/v1/typebots/{typebotId}/analytics/inDepthData',
protect: true,
summary:
'List total answers in blocks and off-default paths visited edges',
tags: ['Analytics'],
},
})
.input(
z.object({
typebotId: z.string(),
timeFilter: z.enum(timeFilterValues).default(defaultTimeFilter),
timeZone: z.string().optional(),
})
)
.output(
z.object({
totalAnswers: z.array(totalAnswersSchema),
offDefaultPathVisitedEdges: z.array(
z.object({ edgeId: z.string(), total: z.number() })
),
})
)
.query(
async ({ input: { typebotId, timeFilter, timeZone }, ctx: { user } }) => {
const typebot = await prisma.typebot.findFirst({
where: canReadTypebots(typebotId, user),
select: { publishedTypebot: true },
})
if (!typebot?.publishedTypebot)
throw new TRPCError({
code: 'NOT_FOUND',
message: 'Published typebot not found',
})

const fromDate = parseFromDateFromTimeFilter(timeFilter, timeZone)
const toDate = parseToDateFromTimeFilter(timeFilter, timeZone)

const totalAnswersPerBlock = await prisma.answer.groupBy({
by: ['blockId', 'resultId'],
where: {
result: {
typebotId: typebot.publishedTypebot.typebotId,
createdAt: fromDate
? {
gte: fromDate,
lte: toDate ?? undefined,
}
: undefined,
},
blockId: {
in: parseGroups(typebot.publishedTypebot.groups, {
typebotVersion: typebot.publishedTypebot.version,
}).flatMap((group) =>
group.blocks.filter(isInputBlock).map((block) => block.id)
),
},
},
})

const totalAnswersV2PerBlock = await prisma.answerV2.groupBy({
by: ['blockId', 'resultId'],
where: {
result: {
typebotId: typebot.publishedTypebot.typebotId,
createdAt: fromDate
? {
gte: fromDate,
lte: toDate ?? undefined,
}
: undefined,
},
blockId: {
in: parseGroups(typebot.publishedTypebot.groups, {
typebotVersion: typebot.publishedTypebot.version,
}).flatMap((group) =>
group.blocks.filter(isInputBlock).map((block) => block.id)
),
},
},
})

const uniqueCounts = totalAnswersPerBlock
.concat(totalAnswersV2PerBlock)
.reduce<{
[key: string]: Set<string>
}>((acc, { blockId, resultId }) => {
acc[blockId] = acc[blockId] || new Set()
acc[blockId].add(resultId)
return acc
}, {})

const offDefaultPathVisitedEdges = await prisma.visitedEdge.groupBy({
by: ['edgeId'],
where: {
result: {
typebotId: typebot.publishedTypebot.typebotId,
createdAt: fromDate
? {
gte: fromDate,
lte: toDate ?? undefined,
}
: undefined,
},
},
_count: { resultId: true },
})

return {
totalAnswers: Object.keys(uniqueCounts).map((blockId) => ({
blockId,
total: uniqueCounts[blockId].size,
})),
offDefaultPathVisitedEdges: offDefaultPathVisitedEdges.map((e) => ({
edgeId: e.edgeId,
total: e._count.resultId,
})),
}
}
)
78 changes: 0 additions & 78 deletions apps/builder/src/features/analytics/api/getTotalAnswers.ts

This file was deleted.

6 changes: 2 additions & 4 deletions apps/builder/src/features/analytics/api/router.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import { router } from '@/helpers/server/trpc'
import { getTotalAnswers } from './getTotalAnswers'
import { getTotalVisitedEdges } from './getTotalVisitedEdges'
import { getStats } from './getStats'
import { getInDepthAnalyticsData } from './getInDepthAnalyticsData'

export const analyticsRouter = router({
getTotalAnswers,
getTotalVisitedEdges,
getInDepthAnalyticsData,
getStats,
})
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,14 @@ import {
useDisclosure,
} from '@chakra-ui/react'
import { useTypebot } from '@/features/editor/providers/TypebotProvider'
import { Stats } from '@typebot.io/schemas'
import React from 'react'
import {
Edge,
GroupV6,
Stats,
TotalAnswers,
TotalVisitedEdges,
} from '@typebot.io/schemas'
import React, { useMemo } from 'react'
import { StatsCards } from './StatsCards'
import { ChangePlanModal } from '@/features/billing/components/ChangePlanModal'
import { Graph } from '@/features/graph/components/Graph'
Expand All @@ -16,6 +22,7 @@ import { trpc } from '@/lib/trpc'
import { isDefined } from '@typebot.io/lib'
import { EventsCoordinatesProvider } from '@/features/graph/providers/EventsCoordinateProvider'
import { timeFilterValues } from '../constants'
import { blockHasItems, isInputBlock } from '@typebot.io/schemas/helpers'

const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone

Expand All @@ -33,7 +40,7 @@ export const AnalyticsGraphContainer = ({
const { t } = useTranslate()
const { isOpen, onOpen, onClose } = useDisclosure()
const { typebot, publishedTypebot } = useTypebot()
const { data } = trpc.analytics.getTotalAnswers.useQuery(
const { data } = trpc.analytics.getInDepthAnalyticsData.useQuery(
{
typebotId: typebot?.id as string,
timeFilter,
Expand All @@ -42,14 +49,36 @@ export const AnalyticsGraphContainer = ({
{ enabled: isDefined(publishedTypebot) }
)

const { data: edgesData } = trpc.analytics.getTotalVisitedEdges.useQuery(
{
typebotId: typebot?.id as string,
timeFilter,
timeZone,
},
{ enabled: isDefined(publishedTypebot) }
)
const totalVisitedEdges = useMemo(() => {
if (
!publishedTypebot?.edges ||
!publishedTypebot.groups ||
!publishedTypebot.events ||
!data?.totalAnswers ||
!stats?.totalViews
)
return
const firstEdgeId = publishedTypebot.events[0].outgoingEdgeId
if (!firstEdgeId) return
return populateEdgesWithVisitData({
edgeId: firstEdgeId,
edges: publishedTypebot.edges,
groups: publishedTypebot.groups,
currentTotalUsers: stats.totalViews,
totalVisitedEdges: data.offDefaultPathVisitedEdges
? [...data.offDefaultPathVisitedEdges]
: [],
totalAnswers: data.totalAnswers,
edgeVisitHistory: [],
})
}, [
data?.offDefaultPathVisitedEdges,
data?.totalAnswers,
publishedTypebot?.edges,
publishedTypebot?.groups,
publishedTypebot?.events,
stats?.totalViews,
])

return (
<Flex
Expand All @@ -73,7 +102,7 @@ export const AnalyticsGraphContainer = ({
typebot={publishedTypebot}
onUnlockProPlanClick={onOpen}
totalAnswers={data?.totalAnswers}
totalVisitedEdges={edgesData?.totalVisitedEdges}
totalVisitedEdges={totalVisitedEdges}
/>
</EventsCoordinatesProvider>
</GraphProvider>
Expand Down Expand Up @@ -102,3 +131,72 @@ export const AnalyticsGraphContainer = ({
</Flex>
)
}

const populateEdgesWithVisitData = ({
edgeId,
edges,
groups,
currentTotalUsers,
totalVisitedEdges,
totalAnswers,
edgeVisitHistory,
}: {
edgeId: string
edges: Edge[]
groups: GroupV6[]
currentTotalUsers: number
totalVisitedEdges: TotalVisitedEdges[]
totalAnswers: TotalAnswers[]
edgeVisitHistory: string[]
}): TotalVisitedEdges[] => {
if (edgeVisitHistory.find((e) => e === edgeId)) return totalVisitedEdges
totalVisitedEdges.push({
edgeId,
total: currentTotalUsers,
})
edgeVisitHistory.push(edgeId)
const edge = edges.find((edge) => edge.id === edgeId)
if (!edge) return totalVisitedEdges
const group = groups.find((group) => edge?.to.groupId === group.id)
if (!group) return totalVisitedEdges
for (const block of edge.to.blockId
? group.blocks.slice(
group.blocks.findIndex((b) => b.id === edge.to.blockId)
)
: group.blocks) {
if (blockHasItems(block)) {
for (const item of block.items) {
if (item.outgoingEdgeId) {
totalVisitedEdges = populateEdgesWithVisitData({
edgeId: item.outgoingEdgeId,
edges,
groups,
currentTotalUsers:
totalVisitedEdges.find(
(tve) => tve.edgeId === item.outgoingEdgeId
)?.total ?? 0,
totalVisitedEdges,
totalAnswers,
edgeVisitHistory,
})
}
}
}
if (block.outgoingEdgeId) {
const totalUsers = isInputBlock(block)
? totalAnswers.find((a) => a.blockId === block.id)?.total
: currentTotalUsers
totalVisitedEdges = populateEdgesWithVisitData({
edgeId: block.outgoingEdgeId,
edges,
groups,
currentTotalUsers: totalUsers ?? 0,
totalVisitedEdges,
totalAnswers,
edgeVisitHistory,
})
}
}

return totalVisitedEdges
}
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ const Expression = ({
case 'Result ID':
case 'Moment of the day':
case 'Environment name':
case 'Transcript':
case 'Yesterday': {
return (
<Text as="span">
Expand Down
Loading

0 comments on commit 40f2120

Please sign in to comment.