|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
INSERT INTO public.narrator_extractions ( |
|
|
id, |
|
|
user_id, |
|
|
hadith_text, |
|
|
extracted_narrators, |
|
|
sanad_chain, |
|
|
success, |
|
|
processing_time_ms, |
|
|
created_at |
|
|
) VALUES |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
CREATE OR REPLACE FUNCTION public.get_extraction_stats() |
|
|
RETURNS TABLE ( |
|
|
total_extractions BIGINT, |
|
|
successful_extractions BIGINT, |
|
|
failed_extractions BIGINT, |
|
|
success_rate NUMERIC, |
|
|
avg_processing_time NUMERIC, |
|
|
total_users BIGINT |
|
|
) AS $$ |
|
|
BEGIN |
|
|
RETURN QUERY |
|
|
SELECT |
|
|
COUNT(*) as total_extractions, |
|
|
COUNT(*) FILTER (WHERE success = true) as successful_extractions, |
|
|
COUNT(*) FILTER (WHERE success = false) as failed_extractions, |
|
|
ROUND( |
|
|
(COUNT(*) FILTER (WHERE success = true)::NUMERIC / COUNT(*)::NUMERIC) * 100, |
|
|
2 |
|
|
) as success_rate, |
|
|
ROUND(AVG(processing_time_ms), 2) as avg_processing_time, |
|
|
COUNT(DISTINCT user_id) as total_users |
|
|
FROM public.narrator_extractions; |
|
|
END; |
|
|
$$ LANGUAGE plpgsql; |
|
|
|
|
|
|
|
|
CREATE OR REPLACE FUNCTION public.get_popular_narrators(limit_count INTEGER DEFAULT 10) |
|
|
RETURNS TABLE ( |
|
|
narrator_name TEXT, |
|
|
analysis_count BIGINT, |
|
|
avg_reliability_grade TEXT, |
|
|
most_common_grade TEXT |
|
|
) AS $$ |
|
|
BEGIN |
|
|
RETURN QUERY |
|
|
SELECT |
|
|
na.narrator_name, |
|
|
COUNT(*) as analysis_count, |
|
|
MODE() WITHIN GROUP (ORDER BY na.reliability_grade) as most_common_grade, |
|
|
ROUND(AVG( |
|
|
CASE |
|
|
WHEN na.reliability_grade = 'Thiqah' THEN 5 |
|
|
WHEN na.reliability_grade = 'Saduq' THEN 4 |
|
|
WHEN na.reliability_grade = 'Da''if' THEN 2 |
|
|
WHEN na.reliability_grade = 'Matruk' THEN 1 |
|
|
ELSE 0 |
|
|
END |
|
|
), 2)::TEXT as avg_reliability_grade |
|
|
FROM public.narrator_analyses na |
|
|
WHERE na.success = true |
|
|
GROUP BY na.narrator_name |
|
|
ORDER BY analysis_count DESC |
|
|
LIMIT limit_count; |
|
|
END; |
|
|
$$ LANGUAGE plpgsql; |
|
|
|
|
|
|
|
|
CREATE OR REPLACE FUNCTION public.get_user_activity(user_uuid UUID) |
|
|
RETURNS TABLE ( |
|
|
total_extractions BIGINT, |
|
|
successful_extractions BIGINT, |
|
|
total_analyses BIGINT, |
|
|
successful_analyses BIGINT, |
|
|
first_activity TIMESTAMP WITH TIME ZONE, |
|
|
last_activity TIMESTAMP WITH TIME ZONE, |
|
|
avg_processing_time NUMERIC |
|
|
) AS $$ |
|
|
BEGIN |
|
|
RETURN QUERY |
|
|
SELECT |
|
|
COUNT(ne.id) as total_extractions, |
|
|
COUNT(ne.id) FILTER (WHERE ne.success = true) as successful_extractions, |
|
|
COUNT(na.id) as total_analyses, |
|
|
COUNT(na.id) FILTER (WHERE na.success = true) as successful_analyses, |
|
|
MIN(LEAST(ne.created_at, na.created_at)) as first_activity, |
|
|
MAX(GREATEST(ne.created_at, na.created_at)) as last_activity, |
|
|
ROUND(AVG(COALESCE(ne.processing_time_ms, na.processing_time_ms)), 2) as avg_processing_time |
|
|
FROM public.narrator_extractions ne |
|
|
FULL OUTER JOIN public.narrator_analyses na ON ne.user_id = na.user_id |
|
|
WHERE ne.user_id = user_uuid OR na.user_id = user_uuid; |
|
|
END; |
|
|
$$ LANGUAGE plpgsql; |
|
|
|
|
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_narrator_extractions_text_search ON public.narrator_extractions |
|
|
USING gin(to_tsvector('arabic', hadith_text)); |
|
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_narrator_analyses_name_search ON public.narrator_analyses |
|
|
USING gin(to_tsvector('arabic', narrator_name)); |
|
|
|
|
|
|
|
|
GRANT EXECUTE ON FUNCTION public.get_extraction_stats() TO authenticated; |
|
|
GRANT EXECUTE ON FUNCTION public.get_popular_narrators(INTEGER) TO authenticated; |
|
|
GRANT EXECUTE ON FUNCTION public.get_user_activity(UUID) TO authenticated; |
|
|
|
|
|
|
|
|
CREATE OR REPLACE FUNCTION public.cleanup_expired_sessions() |
|
|
RETURNS INTEGER AS $$ |
|
|
DECLARE |
|
|
deleted_count INTEGER; |
|
|
BEGIN |
|
|
|
|
|
DELETE FROM public.user_sessions |
|
|
WHERE expires_at < NOW() - INTERVAL '7 days'; |
|
|
|
|
|
GET DIAGNOSTICS deleted_count = ROW_COUNT; |
|
|
RETURN deleted_count; |
|
|
END; |
|
|
$$ LANGUAGE plpgsql; |
|
|
|
|
|
|
|
|
GRANT EXECUTE ON FUNCTION public.cleanup_expired_sessions() TO authenticated; |
|
|
|
|
|
|
|
|
COMMENT ON TABLE public.users IS 'Extended user profiles linked to Supabase auth.users'; |
|
|
COMMENT ON TABLE public.user_sessions IS 'JWT session tracking for security and analytics'; |
|
|
COMMENT ON TABLE public.narrator_extractions IS 'Records of hadith narrator extraction requests and results'; |
|
|
COMMENT ON TABLE public.narrator_analyses IS 'Records of individual narrator analysis requests and results'; |
|
|
|
|
|
COMMENT ON FUNCTION public.get_extraction_stats() IS 'Returns overall statistics about extraction operations'; |
|
|
COMMENT ON FUNCTION public.get_popular_narrators(INTEGER) IS 'Returns most frequently analyzed narrators'; |
|
|
COMMENT ON FUNCTION public.get_user_activity(UUID) IS 'Returns activity summary for a specific user'; |
|
|
COMMENT ON FUNCTION public.cleanup_expired_sessions() IS 'Removes old expired sessions to keep the database clean'; |
|
|
|