diff --git a/packages/backend/src/api/v1/analytics/index.ts b/packages/backend/src/api/v1/analytics/index.ts
index 7874d529..9e4199be 100644
--- a/packages/backend/src/api/v1/analytics/index.ts
+++ b/packages/backend/src/api/v1/analytics/index.ts
@@ -1,4 +1,3 @@
-import { checkAccess } from "@/src/utils/authorization";
import sql from "@/src/utils/db";
import Context from "@/src/utils/koa";
import Router from "koa-router";
@@ -14,6 +13,7 @@ analytics.get("/tokens", async (ctx: Context) => {
const { projectId } = ctx.state;
const { datesQuery, filteredRunsQuery, granularity } = parseQuery(
projectId,
+ ctx.querystring,
ctx.query,
);
@@ -79,6 +79,7 @@ analytics.get("/costs", async (ctx: Context) => {
const { projectId } = ctx.state;
const { datesQuery, filteredRunsQuery, granularity } = parseQuery(
projectId,
+ ctx.querystring,
ctx.query,
);
@@ -144,6 +145,7 @@ analytics.get("/errors", async (ctx: Context) => {
const { projectId } = ctx.state;
const { datesQuery, filteredRunsQuery, granularity } = parseQuery(
projectId,
+ ctx.querystring,
ctx.query,
);
@@ -216,7 +218,7 @@ analytics.get("/users/new", async (ctx: Context) => {
startDate,
endDate,
filteredRunsQuery,
- } = parseQuery(projectId, ctx.query);
+ } = parseQuery(projectId, ctx.querystring, ctx.query);
const distinctMap = {
hourly: sql`distinct on (r.external_user_id, date_trunc('hour', r.created_at at time zone ${timeZone})::timestamp)`,
@@ -539,7 +541,7 @@ analytics.get("/users/active", async (ctx: Context) => {
startDate,
endDate,
filteredRunsQuery,
- } = parseQuery(projectId, ctx.query);
+ } = parseQuery(projectId, ctx.querystring, ctx.query);
const distinctMap = {
hourly: sql`distinct on (r.external_user_id, date_trunc('hour', r.created_at at time zone ${timeZone})::timestamp)`,
@@ -776,7 +778,7 @@ analytics.get("/users/average-cost", async (ctx: Context) => {
localCreatedAt,
startDate,
endDate,
- } = parseQuery(projectId, ctx.query);
+ } = parseQuery(projectId, ctx.querystring, ctx.query);
const [{ stat }] = await sql`
with total_costs as (
@@ -888,6 +890,7 @@ analytics.get("/run-types", async (ctx: Context) => {
const { projectId } = ctx.state;
const { datesQuery, filteredRunsQuery, granularity } = parseQuery(
projectId,
+ ctx.querystring,
ctx.query,
);
@@ -959,7 +962,7 @@ analytics.get("/latency", async (ctx: Context) => {
startDate,
endDate,
timeZone,
- } = parseQuery(projectId, ctx.query);
+ } = parseQuery(projectId, ctx.querystring, ctx.query);
const [{ stat }] = await sql`
select
@@ -1032,6 +1035,7 @@ analytics.get("/feedback-ratio", async (ctx: Context) => {
const { projectId } = ctx.state;
const { datesQuery, filteredRunsQuery, granularity } = parseQuery(
projectId,
+ ctx.querystring,
ctx.query,
);
@@ -1130,9 +1134,12 @@ analytics.get("/models/top", async (ctx: Context) => {
checks: z.string().optional(),
});
const { projectId } = ctx.state;
- const { startDate, endDate, timeZone, userId, name, checks } =
- querySchema.parse(ctx.request.query);
- const filtersQuery = buildFiltersQuery(checks || "");
+ const { startDate, endDate, timeZone, userId, name } = querySchema.parse(
+ ctx.request.query,
+ );
+
+ const deserializedChecks = deserializeLogic(ctx.querystring);
+ const filtersQuery = buildFiltersQuery(deserializedChecks);
let dateFilter = sql``;
if (startDate && endDate && timeZone) {
@@ -1191,7 +1198,8 @@ analytics.get("/templates/top", async (ctx: Context) => {
const { startDate, endDate, timeZone, checks } = querySchema.parse(
ctx.request.query,
);
- const filtersQuery = buildFiltersQuery(checks || "");
+ const deserializedChecks = deserializeLogic(ctx.querystring);
+ const filtersQuery = buildFiltersQuery(deserializedChecks);
const topTemplates = await sql`
select
@@ -1223,22 +1231,13 @@ analytics.get("/templates/top", async (ctx: Context) => {
analytics.get("/languages/top", async (ctx: Context) => {
const { projectId } = ctx.state;
+ ctx.query.granularity = "daily";
const { datesQuery, startDate, endDate, timeZone, filteredRunsQuery } =
- parseQuery(projectId, ctx.query);
+ parseQuery(projectId, ctx.querystring, ctx.query);
const data = await sql`
with dates as (
- select
- *
- from (
- select generate_series(
- ${startDate} at time zone ${timeZone},
- ${endDate} at time zone ${timeZone},
- '1 day'::interval
- )::timestamp as date) t
- where
- date <=current_timestamp at time zone ${timeZone}
-
+ ${datesQuery}
),
filtered_runs as (
${filteredRunsQuery}
@@ -1274,6 +1273,7 @@ analytics.get("/custom-events", async (ctx: Context) => {
const { projectId } = ctx.state;
const { startDate, endDate, timeZone, filteredRunsQuery } = parseQuery(
projectId,
+ ctx.querystring,
ctx.query,
);
diff --git a/packages/backend/src/api/v1/analytics/utils.ts b/packages/backend/src/api/v1/analytics/utils.ts
index 9119d4b3..062e4ed9 100644
--- a/packages/backend/src/api/v1/analytics/utils.ts
+++ b/packages/backend/src/api/v1/analytics/utils.ts
@@ -1,17 +1,18 @@
import { convertChecksToSQL } from "@/src/utils/checks";
import sql from "@/src/utils/db";
-import Context from "@/src/utils/koa";
-import { deserializeLogic } from "shared";
+import { deserializeLogic, LogicNode } from "shared";
import { z } from "zod";
-export function buildFiltersQuery(checks: string) {
- const deserializedChecks = deserializeLogic(checks);
+export function buildFiltersQuery(deserializedChecks: LogicNode) {
return deserializedChecks?.length && deserializedChecks.length > 1
? convertChecksToSQL(deserializedChecks)
: sql`1 = 1`;
}
-export function parseQuery(projectId: string, query: unknown) {
+export function parseQuery(projectId: string, queryString: string, query: any) {
+ const deserializedChecks = deserializeLogic(queryString);
+ const filtersQuery = buildFiltersQuery(deserializedChecks);
+
return z
.object({
startDate: z.string().datetime(),
@@ -26,7 +27,6 @@ export function parseQuery(projectId: string, query: unknown) {
checks: z.string().optional(),
})
.transform(({ startDate, endDate, timeZone, granularity, checks }) => {
- const filtersQuery = buildFiltersQuery(checks || "");
const granularityToIntervalMap = {
hourly: "1 hour",
daily: "1 day",
diff --git a/packages/backend/src/api/v1/external-users.ts b/packages/backend/src/api/v1/external-users.ts
index dba31b17..a6b78f3c 100644
--- a/packages/backend/src/api/v1/external-users.ts
+++ b/packages/backend/src/api/v1/external-users.ts
@@ -1,7 +1,8 @@
+import { checkAccess } from "@/src/utils/authorization";
import sql from "@/src/utils/db";
-import Router from "koa-router";
import { Context } from "koa";
-import { checkAccess } from "@/src/utils/authorization";
+import Router from "koa-router";
+import { deserializeLogic } from "shared";
import { z } from "zod";
import { buildFiltersQuery } from "./analytics/utils";
@@ -112,6 +113,9 @@ users.get(
checks,
} = querySchema.parse(ctx.request.query);
+ const deserializedChecks = deserializeLogic(ctx.querystring);
+ const filtersQuery = buildFiltersQuery(deserializedChecks);
+
let searchQuery = sql``;
if (search) {
searchQuery = sql`and (
@@ -129,8 +133,6 @@ users.get(
`;
}
- const filtersQuery = buildFiltersQuery(checks || "");
-
const sortMapping = {
createdAt: "eu.created_at",
lastSeen: "eu.last_seen",
diff --git a/packages/frontend/pages/users/[[...id]].tsx b/packages/frontend/pages/users/[[...id]].tsx
index d395beeb..c855f9b5 100644
--- a/packages/frontend/pages/users/[[...id]].tsx
+++ b/packages/frontend/pages/users/[[...id]].tsx
@@ -51,6 +51,8 @@ import LineChart from "@/components/analytics/OldLineChart";
import Link from "next/link";
import { parseAsString, useQueryState } from "nuqs";
import { deserializeLogic, getDefaultDateRange } from "shared";
+import ChartComponent from "@/components/analytics/Charts/ChartComponent";
+import AnalyticsCard from "@/components/analytics/AnalyticsCard";
const columns = [
{
@@ -132,13 +134,7 @@ function SelectedUser({ id, onClose }) {
});
}
- const [dateRange, setDateRange] = useLocalStorage({
- key: "dateRange-analytics",
- getInitialValueInEffect: false,
- deserialize: deserializeDateRange,
- defaultValue: getDefaultDateRange(),
- });
- const [startDate, endDate] = dateRange;
+ const [startDate, endDate] = getDefaultDateRange(90);
const [granularity] = useState("daily");
@@ -151,15 +147,6 @@ function SelectedUser({ id, onClose }) {
deserializeLogic(`users=${id}`),
);
- const { data: runCountData, isLoading: runCountLoading } =
- useAnalyticsChartData(
- id && "run-types",
- startDate,
- endDate,
- granularity,
- deserializeLogic(`users=${id}`),
- );
-
const commonChartData: {
startDate: Date;
endDate: Date;
@@ -250,33 +237,57 @@ function SelectedUser({ id, onClose }) {
- d.type === "chat")}
- loading={runCountLoading}
- props={["runs"]}
- splitBy="type"
- agg="sum"
+
+ >
+
+
-
+ >
+
+
- {topModels && (
-
- )}
+
+
+
+
+
}
size="xs"
diff --git a/packages/frontend/utils/dataHooks/analytics.ts b/packages/frontend/utils/dataHooks/analytics.ts
index f2f80532..590e09ce 100644
--- a/packages/frontend/utils/dataHooks/analytics.ts
+++ b/packages/frontend/utils/dataHooks/analytics.ts
@@ -20,7 +20,8 @@ export function useAnalyticsChartData(
secondDimensionKey: string | null = null,
) {
const timeZone = new window.Intl.DateTimeFormat().resolvedOptions().timeZone;
- const checksParam = checks ? `&checks=${serializeLogic(checks)}` : "";
+
+ const checksParam = checks ? `${serializeLogic(checks)}` : "";
const firstDimensionParam = firstDimensionKey
? `&firstDimension=${firstDimensionKey}`
: "";
@@ -30,7 +31,7 @@ export function useAnalyticsChartData(
const { data, isLoading, error } = useProjectSWR(
key
- ? `${getPrefix(key)}?startDate=${startDate.toISOString()}&endDate=${endDate.toISOString()}&timeZone=${timeZone}&granularity=${granularity}${checksParam}${firstDimensionParam}${secondDimensionParam}`
+ ? `${getPrefix(key)}?${checksParam}&startDate=${startDate.toISOString()}&endDate=${endDate.toISOString()}&timeZone=${timeZone}&granularity=${granularity}${firstDimensionParam}${secondDimensionParam}`
: undefined,
);
diff --git a/packages/shared/dashboards/index.ts b/packages/shared/dashboards/index.ts
index 151ca616..744c5b18 100644
--- a/packages/shared/dashboards/index.ts
+++ b/packages/shared/dashboards/index.ts
@@ -1,11 +1,11 @@
-import { LogicNode } from "../checks";
-
-export function getDefaultDateRange() {
+export function getDefaultDateRange(
+ nbDaysFromToday: number = 30,
+): [Date, Date] {
const endOfToday = new Date();
endOfToday.setHours(23, 59, 59, 999);
const oneWeekAgoDate = new Date(endOfToday);
- oneWeekAgoDate.setDate(oneWeekAgoDate.getDate() - 30);
+ oneWeekAgoDate.setDate(oneWeekAgoDate.getDate() - nbDaysFromToday);
oneWeekAgoDate.setHours(0, 0, 0, 0);
const defaultRange: [Date, Date] = [oneWeekAgoDate, endOfToday];
return defaultRange;