CREATE TABLE StudentCourseAnalytics AS
WITH event_summary AS (
SELECT
student_id,
course_id,
COUNT(event_id) AS event_count,
SUM(duration) AS total_duration,
AVG(completed) AS completion_rate,
SUM(
CASE
WHEN resource_type = 'video'
THEN duration
END
) AS video_duration,
SUM(
CASE
WHEN resource_type = 'reading'
THEN duration
END
) AS reading_duration,
MAX(event_time) AS last_activity
FROM LearningEvents
GROUP BY student_id, course_id
),
enrolment_details AS (
SELECT
e.student_id,
e.course_id,
e.mark,
e.engagement_level,
s.degree,
s.entry_year,
s.region,
c.school,
c.credits,
es.event_count,
es.total_duration,
es.completion_rate,
es.video_duration,
es.reading_duration,
es.last_activity,
AVG(e.mark) OVER (
PARTITION BY e.student_id
) AS student_mean_mark,
AVG(e.mark) OVER (
PARTITION BY e.course_id
) AS course_mean_mark
FROM Enrolments AS e
LEFT JOIN Students AS s
ON s.student_id = e.student_id
LEFT JOIN Courses AS c
ON c.course_id = e.course_id
LEFT JOIN event_summary AS es
ON es.student_id = e.student_id
AND es.course_id = e.course_id
),
classified AS (
SELECT
*,
CASE
WHEN event_count IS NULL THEN 'No Activity'
WHEN event_count > 50 THEN 'High'
WHEN event_count > 20 THEN 'Medium'
ELSE 'Low'
END AS engagement_band,
CASE
WHEN event_count IS NULL THEN 'High Risk'
WHEN mark < 40 THEN 'High Risk'
WHEN completion_rate < 0.5 THEN 'Medium Risk'
WHEN event_count < 5 THEN 'Medium Risk'
ELSE 'Low Risk'
END AS risk_band,
mark > student_mean_mark
AS above_student_average,
mark > course_mean_mark
AS above_course_average,
CASE
WHEN last_activity IS NULL THEN 999
ELSE CAST(
julianday('2026-09-01')
- julianday(last_activity)
AS INTEGER
)
END AS days_since_last_activity,
COALESCE(event_count, 0)
AS filled_event_count,
COALESCE(total_duration, 0)
AS filled_total_duration,
COALESCE(completion_rate, 0)
AS filled_completion_rate,
COALESCE(video_duration, 0)
AS filled_video_duration,
COALESCE(reading_duration, 0)
AS filled_reading_duration
FROM enrolment_details
),
degree_statistics AS (
SELECT
degree,
AVG(mark) AS degree_mean_mark,
AVG(filled_event_count) AS degree_mean_events
FROM classified
GROUP BY degree
)
SELECT
classified.student_id,
classified.course_id,
classified.mark,
classified.engagement_level,
classified.degree,
classified.entry_year,
classified.region,
classified.school,
classified.credits,
classified.filled_event_count AS event_count,
classified.filled_total_duration AS total_duration,
classified.filled_completion_rate AS completion_rate,
classified.filled_video_duration AS video_duration,
classified.last_activity,
classified.filled_reading_duration AS reading_duration,
classified.student_mean_mark,
classified.course_mean_mark,
classified.engagement_band,
classified.risk_band,
classified.above_student_average,
classified.above_course_average,
classified.days_since_last_activity,
degree_statistics.degree_mean_mark,
degree_statistics.degree_mean_events
FROM classified
LEFT JOIN degree_statistics
ON degree_statistics.degree = classified.degree;