feat: widgets and ai chat
This commit is contained in:
@@ -1,19 +1,24 @@
|
||||
import {
|
||||
analyticsLoginEvents,
|
||||
and,
|
||||
course,
|
||||
dashOrgStats,
|
||||
db,
|
||||
desc,
|
||||
eq,
|
||||
gte,
|
||||
group,
|
||||
groupmember,
|
||||
isNotNull,
|
||||
lesson,
|
||||
lessonCompletion,
|
||||
organizationmember,
|
||||
profile,
|
||||
sql
|
||||
} from '@db/drizzle';
|
||||
|
||||
import { ROLE } from '@cio/utils/constants';
|
||||
|
||||
export function getCourseStats(orgId: string) {
|
||||
const subquery = db
|
||||
.select({
|
||||
@@ -135,3 +140,62 @@ export function getRecentCertifications(orgId: string) {
|
||||
export function getDashOrgStats(orgId: string) {
|
||||
return db.select().from(dashOrgStats).where(eq(dashOrgStats.orgId, orgId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns login counts grouped by day-of-week (0 = Sunday … 6 = Saturday) for
|
||||
* students of the given organization over the last `days` days.
|
||||
*
|
||||
* Each row in analytics_login_events is already one-per-user-per-day, so the
|
||||
* counts represent distinct active students per weekday — the chart shows
|
||||
* "most active day," not raw login volume.
|
||||
*/
|
||||
export function getOrgStudentLoginsByDayOfWeek(orgId: string, days: number) {
|
||||
const since = sql`CURRENT_DATE - ${sql.raw(`${days}`)}::int`;
|
||||
|
||||
return db
|
||||
.select({
|
||||
dayOfWeek: sql<number>`EXTRACT(DOW FROM ${analyticsLoginEvents.loggedInDate})::int`.as('day_of_week'),
|
||||
count: sql<number>`COUNT(*)::int`.as('count')
|
||||
})
|
||||
.from(analyticsLoginEvents)
|
||||
.innerJoin(organizationmember, eq(organizationmember.profileId, analyticsLoginEvents.userId))
|
||||
.where(
|
||||
and(
|
||||
eq(organizationmember.organizationId, orgId),
|
||||
eq(organizationmember.roleId, ROLE.STUDENT),
|
||||
gte(analyticsLoginEvents.loggedInDate, since)
|
||||
)
|
||||
)
|
||||
.groupBy(sql`day_of_week`)
|
||||
.orderBy(sql`day_of_week`);
|
||||
}
|
||||
|
||||
function getDateKey(date: Date) {
|
||||
return date.toISOString().slice(0, 10);
|
||||
}
|
||||
|
||||
export async function getUserLoginStreak(userId: string) {
|
||||
const rows = await db
|
||||
.select({
|
||||
loggedInDate: analyticsLoginEvents.loggedInDate
|
||||
})
|
||||
.from(analyticsLoginEvents)
|
||||
.where(eq(analyticsLoginEvents.userId, userId))
|
||||
.orderBy(desc(analyticsLoginEvents.loggedInDate))
|
||||
.limit(90);
|
||||
|
||||
const loggedInDates = new Set(rows.map((row) => row.loggedInDate));
|
||||
const cursorDate = new Date();
|
||||
|
||||
if (!loggedInDates.has(getDateKey(cursorDate))) {
|
||||
cursorDate.setDate(cursorDate.getDate() - 1);
|
||||
}
|
||||
|
||||
let daysActive = 0;
|
||||
while (loggedInDates.has(getDateKey(cursorDate))) {
|
||||
daysActive += 1;
|
||||
cursorDate.setDate(cursorDate.getDate() - 1);
|
||||
}
|
||||
|
||||
return { daysActive };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user