| Version 3 (modified by , 2 days ago) ( diff ) |
|---|
Напредни извештаи од базата
1. Целосен Admin Dashboard
Опис: Извештајот прикажува промет и запишувања по месец, детални информации групирани по курс (верзија на курс) за месец, севкупни детални информации за сите курсеви со нивните верзии во текот на целата нивна историја, детални информации за експерти и нивни перформанси
SQL:
-- Monthly total enrollments and revenue
CREATE OR REPLACE FUNCTION dashboard_monthly_totals()
RETURNS TABLE (
year INTEGER,
month INTEGER,
total_enrollments BIGINT,
total_revenue NUMERIC
) AS $$
BEGIN
RETURN QUERY
SELECT
EXTRACT(YEAR FROM e.purchase_date)::INTEGER AS year,
EXTRACT(MONTH FROM e.purchase_date)::INTEGER AS month,
COUNT(e.id)::BIGINT AS total_enrollments,
COALESCE(SUM(p.amount), 0)::NUMERIC AS total_revenue
FROM payment p
JOIN enrollment e ON p.enrollment_id = e.id
GROUP BY
EXTRACT(YEAR FROM e.purchase_date),
EXTRACT(MONTH FROM e.purchase_date)
ORDER BY year DESC, month DESC;
END;
$$ LANGUAGE plpgsql;
-- Monthly course-specific
CREATE OR REPLACE FUNCTION dashboard_monthly_courses()
RETURNS TABLE (
year INTEGER,
month INTEGER,
course_id INTEGER,
course_name TEXT,
course_description TEXT,
course_difficulty TEXT,
course_price NUMERIC,
version_number INTEGER,
is_version_active BOOLEAN,
total_paid_enrollments BIGINT,
total_students BIGINT,
total_revenue NUMERIC,
total_reviews BIGINT,
average_rating NUMERIC
) AS $$
BEGIN
RETURN QUERY
SELECT
EXTRACT(YEAR FROM p.payment_date)::INTEGER AS year,
EXTRACT(MONTH FROM p.payment_date)::INTEGER AS month,
c.id::INTEGER AS course_id,
ct.title_short::TEXT AS course_name,
ct.description_short::TEXT AS course_description,
c.difficulty::TEXT AS course_difficulty,
c.price::NUMERIC AS course_price,
cv.version_number::INTEGER AS version_number,
cv.active::BOOLEAN AS is_version_active,
COUNT(e.id)::BIGINT AS total_paid_enrollments,
COUNT(DISTINCT e.user_id)::BIGINT AS total_students,
SUM(p.amount)::NUMERIC AS total_revenue,
COUNT(r.id)::BIGINT AS total_reviews,
COALESCE(AVG(r.rating), 0)::NUMERIC AS average_rating
FROM course c
JOIN course_translate ct ON c.id = ct.course_id AND ct.language = 'en'
JOIN course_version cv ON c.id = cv.course_id
JOIN enrollment e ON cv.id = e.course_version_id
JOIN payment p ON e.id = p.enrollment_id AND p.payment_status = 'COMPLETED'
LEFT JOIN review r ON e.id = r.enrollment_id
GROUP BY
EXTRACT(YEAR FROM p.payment_date),
EXTRACT(MONTH FROM p.payment_date),
c.id, ct.id, cv.id
ORDER BY year DESC, month DESC, total_revenue DESC, total_students DESC;
END;
$$ LANGUAGE plpgsql;
-- All time course specific, for each course version
CREATE OR REPLACE FUNCTION dashboard_course_performance()
RETURNS TABLE (
course_id INTEGER,
course_name TEXT,
course_description TEXT,
course_difficulty TEXT,
course_price NUMERIC,
version_number INTEGER,
is_version_active BOOLEAN,
total_enrollments BIGINT,
paid_enrollments BIGINT,
trial_enrollments BIGINT,
total_completions BIGINT,
total_students BIGINT,
total_revenue NUMERIC,
average_rating NUMERIC,
total_reviews BIGINT,
avg_days_to_complete INTEGER,
completion_rate_percentage NUMERIC,
avg_lecture_completion_percentage NUMERIC
) AS $$
BEGIN
RETURN QUERY
SELECT
c.id::INTEGER AS course_id,
ct.title_short::TEXT AS course_name,
ct.description_short::TEXT AS course_description,
c.difficulty::TEXT AS course_difficulty,
c.price::NUMERIC AS course_price,
cv.version_number::INTEGER AS version_number,
cv.active::BOOLEAN AS is_version_active,
COUNT(e.id)::BIGINT AS total_enrollments,
COUNT(CASE WHEN p.payment_status = 'COMPLETED' THEN e.id END)::BIGINT AS paid_enrollments,
COUNT(CASE WHEN e.purchase_date IS NULL THEN e.id END)::BIGINT AS trial_enrollments,
COUNT(CASE WHEN e.completion_date IS NOT NULL THEN e.id END)::BIGINT AS total_completions,
COUNT(DISTINCT e.user_id)::BIGINT AS total_students,
COALESCE(SUM(CASE WHEN p.payment_status = 'COMPLETED' THEN p.amount ELSE 0 END), 0)::NUMERIC AS total_revenue,
COALESCE(AVG(r.rating), 0)::NUMERIC AS average_rating,
COUNT(r.id)::BIGINT AS total_reviews,
ROUND(AVG(CASE WHEN e.completion_date IS NOT NULL
THEN EXTRACT(EPOCH FROM e.completion_date - e.activation_date) / 86400
END), 0)::INTEGER AS avg_days_to_complete,
ROUND(100.0 * COUNT(CASE WHEN e.completion_date IS NOT NULL THEN e.id END)::NUMERIC / NULLIF(COUNT(e.id), 0), 2) AS completion_rate_percentage,
ROUND(AVG(
COALESCE(
(SELECT COUNT(CASE WHEN ucp.completed = true THEN 1 END)::NUMERIC * 100.0 /
NULLIF(COUNT(*), 0)
FROM user_course_progress ucp
WHERE ucp.enrollment_id = e.id), 0
)
), 2)::NUMERIC AS avg_lecture_completion_percentage
FROM course c
JOIN course_translate ct ON c.id = ct.course_id AND ct.language = 'en'
JOIN course_version cv ON c.id = cv.course_id
LEFT JOIN enrollment e ON cv.id = e.course_version_id -- left join to include versions with zero enrollments
LEFT JOIN payment p ON e.id = p.enrollment_id -- left join to include enrollments without payments (because enrollment can be in trial)
LEFT JOIN review r ON e.id = r.enrollment_id -- left join to include enrollments without reviews
GROUP BY c.id, ct.id, cv.id
HAVING COUNT(DISTINCT e.id) > 0
ORDER BY total_revenue DESC, completion_rate_percentage DESC;
END;
$$ LANGUAGE plpgsql;
-- Expert performance summary
CREATE OR REPLACE FUNCTION dashboard_expert_performance()
RETURNS TABLE (
expert_id INTEGER,
expert_name TEXT,
courses_created BIGINT,
total_enrollments BIGINT,
paid_enrollments BIGINT,
trial_enrollments BIGINT,
total_revenue NUMERIC,
avg_rating NUMERIC,
total_reviews BIGINT
) AS $$
BEGIN
RETURN QUERY
SELECT
ex.id::INTEGER AS expert_id,
ex.name::TEXT,
COUNT(DISTINCT c.id)::BIGINT AS courses_created,
COUNT(DISTINCT e.id)::BIGINT AS total_enrollments,
COUNT(DISTINCT CASE WHEN p.payment_status = 'COMPLETED' THEN e.id END)::BIGINT AS paid_enrollments,
COUNT(DISTINCT CASE WHEN e.purchase_date IS NULL THEN e.id END)::BIGINT AS trial_enrollments,
COALESCE(SUM(CASE WHEN p.payment_status = 'COMPLETED' THEN p.amount ELSE 0 END), 0)::NUMERIC AS total_revenue,
COALESCE(AVG(r.rating), 0)::NUMERIC AS avg_rating,
COUNT(r.id)::BIGINT AS total_reviews
FROM expert ex
LEFT JOIN expert_course ec ON ex.id = ec.expert_id -- experts with no courses should be included
LEFT JOIN course c ON ec.course_id = c.id -- experts with no courses should be included
JOIN course_version cv ON c.id = cv.course_id -- only courses with versions (which is always the case)
LEFT JOIN enrollment e ON cv.id = e.course_version_id -- left join to include courses with zero enrollments
LEFT JOIN payment p ON e.id = p.enrollment_id -- left join to include enrollments without payments
LEFT JOIN review r ON e.id = r.enrollment_id -- left join to include enrollments without reviews
GROUP BY ex.id
ORDER BY total_revenue DESC NULLS LAST;
END;
$$ LANGUAGE plpgsql;
Релациона алгебра:
-- Monthly total enrollments and revenue
π year, month, total_enrollments, total_revenue (
τ year DESC, month DESC (
γ YEAR(purchase_date), MONTH(purchase_date);
year ← YEAR(purchase_date),
month ← MONTH(purchase_date),
total_enrollments ← COUNT(id),
total_revenue ← COALESCE(SUM(amount), 0) (
payment ⋈ enrollment_id = id enrollment
)
)
)
-- Monthly course-specific
π year, month, course_id, course_name, course_description, course_difficulty, course_price, version_number, is_version_active, total_paid_enrollments, total_students, total_revenue, total_reviews, average_rating (
τ year DESC, month DESC, total_revenue DESC, total_students DESC (
γ YEAR(payment_date), MONTH(payment_date), c.id, ct.id, cv.id;
year ← YEAR(payment_date),
month ← MONTH(payment_date),
course_id ← c.id,
course_name ← ct.title_short,
course_description ← ct.description_short,
course_difficulty ← c.difficulty,
course_price ← c.price,
version_number ← cv.version_number,
is_version_active ← cv.active,
total_paid_enrollments ← COUNT(e.id),
total_students ← COUNT(DISTINCT e.user_id),
total_revenue ← SUM(p.amount),
total_reviews ← COUNT(r.id),
average_rating ← COALESCE(AVG(r.rating), 0)
(
course ⋈ id = course_id ∧ language = 'en' course_translate
⋈ id = course_id course_version
⋈ id = course_version_id enrollment
⋈ id = enrollment_id ∧ payment_status = 'COMPLETED' payment
⟕ id = enrollment_id review
)
)
)
-- All time course specific, for each course version
π course_id, course_name, course_description, course_difficulty, course_price, version_number, is_version_active, total_enrollments, paid_enrollments, trial_enrollments, total_completions, total_students, total_revenue, average_rating, total_reviews, avg_days_to_complete, completion_rate_percentage, avg_lecture_completion_percentage (
τ total_revenue DESC, completion_rate_percentage DESC (
σ COUNT(DISTINCT e.id) > 0 (
γ c.id, ct.id, cv.id;
course_id ← c.id,
course_name ← ct.title_short,
course_description ← ct.description_short,
course_difficulty ← c.difficulty,
course_price ← c.price,
version_number ← cv.version_number,
is_version_active ← cv.active,
total_enrollments ← COUNT(e.id),
paid_enrollments ← COUNT(CASE WHEN payment_status = 'COMPLETED' THEN e.id END),
trial_enrollments ← COUNT(CASE WHEN purchase_date IS NULL THEN e.id END),
total_completions ← COUNT(CASE WHEN completion_date IS NOT NULL THEN e.id END),
total_students ← COUNT(DISTINCT e.user_id),
total_revenue ← COALESCE(SUM(CASE WHEN payment_status = 'COMPLETED' THEN amount ELSE 0 END), 0),
average_rating ← COALESCE(AVG(r.rating), 0),
total_reviews ← COUNT(r.id),
avg_days_to_complete ← ROUND(AVG(CASE WHEN completion_date IS NOT NULL THEN EXTRACT(EPOCH FROM completion_date - activation_date) / 86400 END), 0),
completion_rate_percentage ← ROUND(100.0 * COUNT(CASE WHEN completion_date IS NOT NULL THEN e.id END) / NULLIF(COUNT(e.id), 0), 2),
avg_lecture_completion_percentage ← ROUND(AVG(COALESCE((SELECT COUNT(CASE WHEN completed = true THEN 1 END) * 100.0 / NULLIF(COUNT(*), 0) FROM user_course_progress WHERE enrollment_id = e.id), 0)), 2)
(
course ⋈ id = course_id ∧ language = 'en' course_translate
⋈ id = course_id course_version
⟕ id = course_version_id enrollment
⟕ id = enrollment_id payment
⟕ id = enrollment_id review
)
)
)
)
-- Expert performance summary
π expert_id, name, courses_created, total_enrollments, paid_enrollments, trial_enrollments, total_revenue, avg_rating, total_reviews (
τ total_revenue DESC NULLS LAST (
γ ex.id;
expert_id ← ex.id,
name ← ex.name,
courses_created ← COUNT(DISTINCT c.id),
total_enrollments ← COUNT(DISTINCT e.id),
paid_enrollments ← COUNT(DISTINCT CASE WHEN payment_status = 'COMPLETED' THEN e.id END),
trial_enrollments ← COUNT(DISTINCT CASE WHEN purchase_date IS NULL THEN e.id END),
total_revenue ← COALESCE(SUM(CASE WHEN payment_status = 'COMPLETED' THEN amount ELSE 0 END), 0),
avg_rating ← COALESCE(AVG(r.rating), 0),
total_reviews ← COUNT(r.id)
(
expert
⟕ id = expert_id expert_course
⟕ course_id = id course
⋈ id = course_id course_version
⟕ id = course_version_id enrollment
⟕ id = enrollment_id payment
⟕ id = enrollment_id review
)
)
)
Note:
See TracWiki
for help on using the wiki.
