feat: move course analytics request to api

This commit is contained in:
Rotimi Best
2025-12-05 11:20:12 +01:00
parent 97d9e490b0
commit ac1a05f006
2 changed files with 51 additions and 27 deletions
@@ -1,20 +1,39 @@
import type { RequestHandler } from './$types';
import type { CourseAnalytics, StudentOverview } from '$lib/utils/types/analytics';
import type { UserExercisesStats } from '$lib/utils/types/analytics';
import { calcPercentageWithRounding } from '$lib/utils/functions/number.js';
import { fetchProfileCourseProgress } from '$lib/utils/services/courses';
import { getServerSupabase } from '$lib/utils/functions/supabase.server';
import { checkUserCoursePermissions } from '$lib/utils/functions/permissions';
import { json } from '@sveltejs/kit';
const supabase = getServerSupabase();
const CACHE_DURATION = 60 * 5; // 5 minutes
export async function POST({ setHeaders, request }) {
const { courseId } = await request.json();
export const GET: RequestHandler = async ({ setHeaders, request }) => {
const userId = request.headers.get('user_id');
if (!userId) {
return json({ success: false, message: 'Unauthorized' }, { status: 401 });
}
const url = new URL(request.url);
const courseId = url.searchParams.get('courseId');
if (!courseId) {
return json({ success: false, message: 'Request is missing required fields' }, { status: 400 });
return json({ success: false, message: 'Course ID is required' }, { status: 400 });
}
try {
const supabase = getServerSupabase();
const hasPermission = await checkUserCoursePermissions(supabase, userId, courseId);
if (!hasPermission) {
return json(
{
success: false,
message: 'Access denied. User is not a member of this course or organization.'
},
{ status: 403 }
);
}
setHeaders({
@@ -22,16 +41,15 @@ export async function POST({ setHeaders, request }) {
'content-type': 'application/json'
});
try {
const analytics = await getCourseAnalytics(courseId);
return json(analytics, { status: 200 });
const analytics = await getCourseAnalytics(supabase, courseId);
return json({ success: true, data: analytics });
} catch (error) {
console.error('Course analytics error:', error);
return json({ error: 'Something went wrong' }, { status: 500 });
return json({ success: false, message: 'Internal server error' }, { status: 500 });
}
}
};
async function getCourseAnalytics(courseId: string): Promise<CourseAnalytics> {
async function getCourseAnalytics(supabase: any, courseId: string): Promise<CourseAnalytics> {
const analytics: CourseAnalytics = {
totalTutors: 0,
totalStudents: 0,
@@ -93,7 +111,7 @@ async function getCourseAnalytics(courseId: string): Promise<CourseAnalytics> {
const studentAnalytics = await Promise.all(
members
.filter((member) => member.role_id === 3)
.map((member) => getStudentOverview(courseId, member))
.map((member) => getStudentOverview(supabase, courseId, member))
);
analytics.students = studentAnalytics.filter(
@@ -126,7 +144,7 @@ async function getCourseAnalytics(courseId: string): Promise<CourseAnalytics> {
return analytics;
}
async function getLastLogin(userId: string): Promise<string | undefined> {
async function getLastLogin(supabase: any, userId: string): Promise<string | undefined> {
try {
const { data, error } = await supabase
.from('analytics_login_events')
@@ -144,7 +162,11 @@ async function getLastLogin(userId: string): Promise<string | undefined> {
}
}
async function getStudentOverview(courseId: string, member: any): Promise<StudentOverview | null> {
async function getStudentOverview(
supabase: any,
courseId: string,
member: any
): Promise<StudentOverview | null> {
try {
// Use the same service function that the working analytics/user API uses
const { data: courseProgressData, error: progressError } = await fetchProfileCourseProgress(
@@ -164,7 +186,7 @@ async function getStudentOverview(courseId: string, member: any): Promise<Studen
}
// Fetch exercise stats using the same method as the people page
const userExercisesStats = await fetchUserExercisesStats(courseId, member.profile_id);
const userExercisesStats = await fetchUserExercisesStats(supabase, courseId, member.profile_id);
// Calculate exercise completion using the same logic as people page
const completedExercises =
@@ -172,7 +194,7 @@ async function getStudentOverview(courseId: string, member: any): Promise<Studen
const totalExercises = userExercisesStats?.length || 0;
// Get last login using the same approach as analytics/user API
const lastLoginDate = await getLastLogin(member.profile_id);
const lastLoginDate = await getLastLogin(supabase, member.profile_id);
// Format last seen
let lastSeen = 'Never';
@@ -225,6 +247,7 @@ async function getStudentOverview(courseId: string, member: any): Promise<Studen
}
async function fetchUserExercisesStats(
supabase: any,
courseId: string,
userId: string
): Promise<UserExercisesStats[] | undefined> {
@@ -28,17 +28,18 @@
isLoading = true;
const accessToken = await getAccessToken();
const response = await fetch('/api/analytics/course', {
method: 'POST',
const response = await fetch(`/api/courses/analytics?courseId=${data.courseId}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: accessToken
},
body: JSON.stringify({ courseId: data.courseId })
}
});
if (response.ok) {
courseAnalytics = (await response.json()) as CourseAnalytics;
const { data } = await response.json();
courseAnalytics = data as CourseAnalytics;
} else {
console.error('Failed to fetch course analytics:', response);
snackbar.error('Failed to load analytics data');