Skip to content

Commit 39873d9

Browse files
committed
feat: Return a view to the project analytics
1 parent bb54ddd commit 39873d9

4 files changed

Lines changed: 82 additions & 4 deletions

File tree

backend/src/analytics/db.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { User } from "@/auth/validation";
22
import { PoolClient } from "pg";
3-
import { UserAnalytics, UserSession } from "./types";
3+
import { UserAnalytics, UserProjectAnalytics, UserSession } from "./types";
44
import { WindowSchemaType } from "./validation";
55

66
const getUserAnalytics = async (
@@ -17,6 +17,25 @@ const getUserAnalytics = async (
1717
return data.rows[0];
1818
};
1919

20+
const getUserProjectAnalytics = async (
21+
client: PoolClient,
22+
user_id: User["user_id"],
23+
window: WindowSchemaType,
24+
project_path: string
25+
): Promise<UserProjectAnalytics> => {
26+
const { count, unit } = window.interval;
27+
const interval = `${count} ${unit}`;
28+
const data = await client.query({
29+
text: `select lang_durations, machine_durations, editor_durations, files_durations, activity_durations, work_duration_ms, active_days, coalesce(
30+
NULLIF(regexp_replace($2::varchar, '.*/', ''), ''),
31+
'Unknown Project'
32+
) AS project_name
33+
from user_project_analytics_aggregate_period($1, $2, $3, $4::interval)`,
34+
values: [user_id, project_path, window.start, interval],
35+
});
36+
return data.rows[0];
37+
};
38+
2039
const getOverlappingUserProjectSessions = async (
2140
client: PoolClient,
2241
user_id: User["user_id"],
@@ -50,6 +69,7 @@ const db = {
5069
getUserAnalytics,
5170
getOverlappingUserProjectSessions,
5271
getOverlappingUserLangSessions,
72+
getUserProjectAnalytics,
5373
};
5474

5575
export default db;

backend/src/analytics/service.ts

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import { User } from "@/auth/validation";
22
import pool from "@/pool";
3-
import { NormalizedUserSession, UserAnalytics } from "./types";
3+
import {
4+
NormalizedUserSession,
5+
UserAnalytics,
6+
UserProjectAnalytics,
7+
} from "./types";
48
import { WindowSchemaType } from "./validation";
59
import db from "./db";
610
import { normalizeSession } from "./utils";
@@ -19,6 +23,26 @@ const getUserAnalytics = async (
1923
}
2024
};
2125

26+
const getUserProjectAnalytics = async (
27+
user_id: User["user_id"],
28+
project_path: string,
29+
window: WindowSchemaType
30+
): Promise<UserProjectAnalytics> => {
31+
const client = await pool.connect();
32+
33+
try {
34+
const data = await db.getUserProjectAnalytics(
35+
client,
36+
user_id,
37+
window,
38+
project_path
39+
);
40+
return data;
41+
} finally {
42+
client.release();
43+
}
44+
};
45+
2246
const getUserLangSessions = async (
2347
user_id: User["user_id"],
2448
window: WindowSchemaType
@@ -54,4 +78,9 @@ const getUserProjectSessions = async (
5478
client.release();
5579
}
5680
};
57-
export { getUserAnalytics, getUserProjectSessions, getUserLangSessions };
81+
export {
82+
getUserAnalytics,
83+
getUserProjectSessions,
84+
getUserLangSessions,
85+
getUserProjectAnalytics,
86+
};

backend/src/analytics/types.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ export type UserAnalytics = Prettify<{
1414
active_days: number;
1515
}>;
1616

17+
export type UserProjectAnalytics = UserAnalytics & {
18+
files_durations: Duration;
19+
};
20+
1721
export type UserSession<T> = T & {
1822
window_start: Date;
1923
window_end: Date;

backend/src/index.ts

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,11 @@ import {
2020
import * as heartbeat from "./heartbeat/service";
2121
import * as analytics from "./analytics/service";
2222
import { WindowSchema, WindowSchemaInputType } from "./analytics/validation";
23-
import { NormalizedUserSession, UserAnalytics } from "./analytics/types";
23+
import {
24+
NormalizedUserSession,
25+
UserAnalytics,
26+
UserProjectAnalytics,
27+
} from "./analytics/types";
2428

2529
export const graphql = {
2630
Query: {
@@ -43,6 +47,27 @@ export const graphql = {
4347
return SuccessResponse<UserAnalytics>(data);
4448
}
4549
),
50+
ProjectAnaltyics: authorized(
51+
async (
52+
user,
53+
project_path: string,
54+
window: WindowSchemaInputType
55+
): Promise<AppResponse<UserProjectAnalytics>> => {
56+
// validate first
57+
const parsed_window = WindowSchema.safeParse(window);
58+
if (parsed_window.error) {
59+
return ErrorResponse(
60+
parsed_window.error.issues.map((x) => x.message)
61+
);
62+
}
63+
const data = await analytics.getUserProjectAnalytics(
64+
user.user_id,
65+
project_path,
66+
parsed_window.data
67+
);
68+
return SuccessResponse<UserProjectAnalytics>(data);
69+
}
70+
),
4671
UserLangSessions: authorized(
4772
async (
4873
user,

0 commit comments

Comments
 (0)