@@ -0,0 +1,11 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
let count = 0;
|
||||||
|
|
||||||
|
function handleClick() {
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<button on:click={handleClick}>
|
||||||
|
clicks: {count}
|
||||||
|
</button>
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"name": "svelte-ui",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"description": "Styles and components for use in svelte websites",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"lint": "prettier --check --ignore-path=../../.prettierignore .",
|
||||||
|
"format": "prettier --write --ignore-path=../../.prettierignore ."
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"eslint-config-custom": "workspace:*",
|
||||||
|
"svelte": "^3.9.2"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "MIT"
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1 @@
|
|||||||
|
alter table "public"."profile" alter column "avatar_url" set default 'https://pgrest.classroomio.com/storage/v1/object/public/avatars/avatar.png'::text;
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
|
||||||
|
drop function if exists "public"."get_courses"(org_id_arg uuid, profile_id_arg uuid);
|
||||||
|
|
||||||
|
set check_function_bodies = off;
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION public.get_courses(org_id_arg uuid, profile_id_arg uuid)
|
||||||
|
RETURNS TABLE(id uuid, org_id uuid, title character varying, slug character varying, description character varying, logo text, banner_image text, cost bigint, currency character varying, is_published boolean, profile_id uuid, role_id bigint, total_lessons bigint, total_students bigint, progress_rate bigint)
|
||||||
|
LANGUAGE plpgsql
|
||||||
|
AS $function$
|
||||||
|
BEGIN
|
||||||
|
Return query
|
||||||
|
select course.id, organization.id AS org_id, course.title, course.slug, course.description, course.logo, course.banner_image, course.cost, course.currency, course.is_published, groupmember.profile_id, groupmember.role_id, (select COUNT(*) from lesson as l where l.course_id = course.id) AS total_lessons, (select COUNT(*) from groupmember as gm where gm.group_id = course.group_id AND gm.role_id = 3) as total_students, (select COUNT(*) from lesson_completion as lc join lesson as l on l.id = lc.lesson_id where l.course_id = course.id and lc.is_complete = true and lc.profile_id = profile_id_arg) AS progress_rate
|
||||||
|
from course
|
||||||
|
join "group" on "group".id = course.group_id
|
||||||
|
join groupmember on groupmember.group_id = course.group_id
|
||||||
|
join organization on organization.id = "group".organization_id
|
||||||
|
where course.status = 'ACTIVE' AND organization.id = org_id_arg
|
||||||
|
-- GROUP BY course.id, groupmember.profile_id
|
||||||
|
ORDER BY course.created_at DESC;
|
||||||
|
END;
|
||||||
|
$function$
|
||||||
|
;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
alter table "public"."course" alter column "metadata" set default '{"goals": "", "description": "", "requirements": ""}'::jsonb;
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
alter table "public"."community_answer" add column "author_profile_id" uuid;
|
||||||
|
|
||||||
|
alter table "public"."community_question" add column "author_profile_id" uuid;
|
||||||
|
|
||||||
|
alter table "public"."community_answer" add constraint "community_answer_author_profile_id_fkey" FOREIGN KEY (author_profile_id) REFERENCES profile(id) not valid;
|
||||||
|
|
||||||
|
alter table "public"."community_answer" validate constraint "community_answer_author_profile_id_fkey";
|
||||||
|
|
||||||
|
alter table "public"."community_question" add constraint "community_question_author_profile_id_fkey" FOREIGN KEY (author_profile_id) REFERENCES profile(id) not valid;
|
||||||
|
|
||||||
|
alter table "public"."community_question" validate constraint "community_question_author_profile_id_fkey";
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
create table "public"."course_newsfeed" (
|
||||||
|
"created_at" timestamp with time zone not null default now(),
|
||||||
|
"author_id" uuid,
|
||||||
|
"content" text,
|
||||||
|
"id" uuid not null default gen_random_uuid(),
|
||||||
|
"course_id" uuid,
|
||||||
|
"reaction" jsonb default '{"clap": [], "smile": [], "thumbsup": [], "thumbsdown": []}'::jsonb,
|
||||||
|
"is_pinned" boolean not null default false
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
create table "public"."course_newsfeed_comment" (
|
||||||
|
"created_at" timestamp with time zone not null default now(),
|
||||||
|
"author_id" uuid,
|
||||||
|
"content" text,
|
||||||
|
"id" bigint generated by default as identity not null,
|
||||||
|
"course_newsfeed_id" uuid
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX course_newsfeed_comment_id_key ON public.course_newsfeed_comment USING btree (id);
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX course_newsfeed_comment_pkey ON public.course_newsfeed_comment USING btree (id);
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX course_newsfeed_pkey ON public.course_newsfeed USING btree (id);
|
||||||
|
|
||||||
|
alter table "public"."course_newsfeed" add constraint "course_newsfeed_pkey" PRIMARY KEY using index "course_newsfeed_pkey";
|
||||||
|
|
||||||
|
alter table "public"."course_newsfeed_comment" add constraint "course_newsfeed_comment_pkey" PRIMARY KEY using index
|
||||||
|
"course_newsfeed_comment_pkey";
|
||||||
|
|
||||||
|
alter table "public"."course_newsfeed" add constraint "course_newsfeed_author_id_fkey" FOREIGN KEY (author_id) REFERENCES groupmember(id) not valid;
|
||||||
|
|
||||||
|
alter table "public"."course_newsfeed" validate constraint "course_newsfeed_author_id_fkey";
|
||||||
|
|
||||||
|
alter table "public"."course_newsfeed" add constraint "course_newsfeed_course_id_fkey" FOREIGN KEY (course_id) REFERENCES course(id) not valid;
|
||||||
|
|
||||||
|
alter table "public"."course_newsfeed" validate constraint "course_newsfeed_course_id_fkey";
|
||||||
|
|
||||||
|
alter table "public"."course_newsfeed_comment" add constraint "course_newsfeed_comment_id_key" UNIQUE using index "course_newsfeed_comment_id_key";
|
||||||
|
|
||||||
|
alter table "public"."course_newsfeed_comment" add constraint "course_newsfeed_comment_author_id_fkey" FOREIGN KEY (author_id) REFERENCES groupmember(id) not valid;
|
||||||
|
|
||||||
|
alter table "public"."course_newsfeed_comment" validate constraint "course_newsfeed_comment_author_id_fkey";
|
||||||
|
|
||||||
|
alter table "public"."course_newsfeed_comment" add constraint "course_newsfeed_comment_course_newsfeed_id_fkey" FOREIGN KEY (course_newsfeed_id) REFERENCES course_newsfeed(id) not valid;
|
||||||
|
|
||||||
|
alter table "public"."course_newsfeed_comment" validate constraint "course_newsfeed_comment_course_newsfeed_id_fkey";
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
create sequence "public"."test_tenant_id_seq";
|
||||||
|
|
||||||
|
create table "public"."test_tenant" (
|
||||||
|
"id" integer not null default nextval('test_tenant_id_seq'::regclass),
|
||||||
|
"details" text
|
||||||
|
);
|
||||||
|
|
||||||
|
truncate "public"."community_question" cascade;
|
||||||
|
|
||||||
|
alter table "public"."community_question" add column "course_id" uuid not null;
|
||||||
|
|
||||||
|
alter sequence "public"."test_tenant_id_seq" owned by "public"."test_tenant"."id";
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX test_tenant_pkey ON public.test_tenant USING btree (id);
|
||||||
|
|
||||||
|
alter table "public"."test_tenant" add constraint "test_tenant_pkey" PRIMARY KEY using index "test_tenant_pkey";
|
||||||
|
|
||||||
|
alter table "public"."community_question" add constraint "community_question_course_id_fkey" FOREIGN KEY (course_id) REFERENCES course(id) not valid;
|
||||||
|
|
||||||
|
alter table "public"."community_question" validate constraint "community_question_course_id_fkey";
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
alter table "public"."profile" add column "is_email_verified" boolean default false;
|
||||||
|
alter table "public"."profile" add column "verified_at" timestamp with time zone;
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX profile_email_key ON public.profile USING btree (email);
|
||||||
|
|
||||||
|
alter table "public"."profile" add constraint "profile_email_key" UNIQUE using index "profile_email_key";
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
create type "public"."LOCALE" as enum ('en', 'hi', 'fr', 'pt', 'de', 'vi', 'ru', 'es');
|
||||||
|
|
||||||
|
create table
|
||||||
|
public.lesson_language (
|
||||||
|
id bigint generated by default as identity,
|
||||||
|
content text null,
|
||||||
|
lesson_id uuid null default gen_random_uuid (),
|
||||||
|
locale "LOCALE" default 'en'::"LOCALE",
|
||||||
|
constraint lesson_language_pkey primary key (id),
|
||||||
|
constraint public_lesson_language_lesson_id_fkey foreign key (lesson_id) references lesson (id)
|
||||||
|
) tablespace pg_default;
|
||||||
|
|
||||||
|
alter table "public"."profile" add column "locale" "LOCALE" default 'en'::"LOCALE";
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
create type "public"."PLAN" as enum ('EARLY_ADOPTER', 'ENTERPRISE', 'BASIC');
|
||||||
|
|
||||||
|
create table "public"."organization_plan" (
|
||||||
|
"id" bigint generated by default as identity not null,
|
||||||
|
"activated_at" timestamp with time zone not null default now(),
|
||||||
|
"org_id" uuid,
|
||||||
|
"plan_name" "PLAN",
|
||||||
|
"is_active" boolean,
|
||||||
|
"deactivated_at" timestamp with time zone,
|
||||||
|
"updated_at" timestamp with time zone default now(),
|
||||||
|
"payload" jsonb,
|
||||||
|
"triggered_by" bigint
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX organization_plan_pkey ON public.organization_plan USING btree (id);
|
||||||
|
|
||||||
|
alter table "public"."organization_plan" add constraint "organization_plan_pkey" PRIMARY KEY using index "organization_plan_pkey";
|
||||||
|
|
||||||
|
alter table "public"."organization_plan" add constraint "organization_plan_org_id_fkey" FOREIGN KEY (org_id) REFERENCES organization(id) not valid;
|
||||||
|
|
||||||
|
alter table "public"."organization_plan" validate constraint "organization_plan_org_id_fkey";
|
||||||
|
|
||||||
|
alter table "public"."organization_plan" add constraint "organization_plan_triggered_by_fkey" FOREIGN KEY (triggered_by) REFERENCES organizationmember(id) not valid;
|
||||||
|
|
||||||
|
alter table "public"."organization_plan" validate constraint "organization_plan_triggered_by_fkey";
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
alter table "public"."submission" add column "feedback" text;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
alter table "public"."organization" add column "customization" json not null default '{"apps":{"poll":true,"comments":true},"course":{"grading":true,"newsfeed":true},"dashboard":{"exercise":true,"community":true,"bannerText":"","bannerImage":""}}'::json;
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
create type "public"."COURSE_TYPE" as enum ('SELF_PACED', 'LIVE_CLASS');
|
||||||
|
|
||||||
|
alter table "public"."course" add column "type" "COURSE_TYPE" default 'LIVE_CLASS'::"COURSE_TYPE";
|
||||||
|
|
||||||
|
drop function if exists "public"."get_courses"(org_id_arg uuid, profile_id_arg uuid);
|
||||||
|
|
||||||
|
create
|
||||||
|
or replace function public.get_courses (org_id_arg uuid, profile_id_arg uuid) returns table (
|
||||||
|
id uuid,
|
||||||
|
org_id uuid,
|
||||||
|
title character varying,
|
||||||
|
slug character varying,
|
||||||
|
description character varying,
|
||||||
|
logo text,
|
||||||
|
banner_image text,
|
||||||
|
cost bigint,
|
||||||
|
currency character varying,
|
||||||
|
is_published boolean,
|
||||||
|
profile_id uuid,
|
||||||
|
role_id bigint,
|
||||||
|
total_lessons bigint,
|
||||||
|
total_students bigint,
|
||||||
|
progress_rate bigint,
|
||||||
|
type "COURSE_TYPE"
|
||||||
|
) language plpgsql as $function$
|
||||||
|
BEGIN
|
||||||
|
Return query
|
||||||
|
select course.id, organization.id AS org_id, course.title, course.slug, course.description, course.logo, course.banner_image, course.cost, course.currency, course.is_published, groupmember.profile_id, groupmember.role_id, (select COUNT(*) from lesson as l where l.course_id = course.id) AS total_lessons, (select COUNT(*) from groupmember as gm where gm.group_id = course.group_id AND gm.role_id = 3) as total_students, (select COUNT(*) from lesson_completion as lc join lesson as l on l.id = lc.lesson_id where l.course_id = course.id and lc.is_complete = true and lc.profile_id = profile_id_arg) AS progress_rate, course.type as type
|
||||||
|
from course
|
||||||
|
join "group" on "group".id = course.group_id
|
||||||
|
join groupmember on groupmember.group_id = course.group_id
|
||||||
|
join organization on organization.id = "group".organization_id
|
||||||
|
where course.status = 'ACTIVE' AND organization.id = org_id_arg
|
||||||
|
-- GROUP BY course.id, groupmember.profile_id
|
||||||
|
ORDER BY course.created_at DESC;
|
||||||
|
END;
|
||||||
|
$function$;
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
set check_function_bodies = off;
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION public.get_course_progress(course_id_arg uuid, profile_id_arg uuid)
|
||||||
|
RETURNS TABLE(lessons_count bigint, lessons_completed bigint, exercises_count bigint, exercises_completed bigint)
|
||||||
|
LANGUAGE plpgsql
|
||||||
|
AS $function$
|
||||||
|
BEGIN
|
||||||
|
RETURN QUERY
|
||||||
|
select
|
||||||
|
count(lesson.id) as lessons_count,
|
||||||
|
count(lesson_completion.id) as lessons_completed,
|
||||||
|
count(exercise.id) as exercises_count,
|
||||||
|
count(submission.id) as exercises_completed
|
||||||
|
from
|
||||||
|
course
|
||||||
|
join "group" on "group".id = course.group_id
|
||||||
|
join groupmember on groupmember.group_id = course.group_id
|
||||||
|
join profile on profile.id = groupmember.profile_id
|
||||||
|
left join lesson on lesson.course_id = course.id
|
||||||
|
left join lesson_completion on lesson_completion.lesson_id = lesson.id
|
||||||
|
and lesson_completion.is_complete = true
|
||||||
|
and lesson_completion.profile_id = profile.id
|
||||||
|
left join exercise on exercise.lesson_id = lesson.id
|
||||||
|
left join submission on submission.exercise_id = exercise.id
|
||||||
|
and submission.submitted_by = groupmember.id
|
||||||
|
where
|
||||||
|
course.id = course_id_arg
|
||||||
|
and profile.id = profile_id_arg;
|
||||||
|
END;
|
||||||
|
$function$
|
||||||
|
;
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
drop function if exists "public"."get_courses"(org_id_arg uuid, profile_id_arg uuid);
|
||||||
|
|
||||||
|
create
|
||||||
|
or replace function public.get_courses (org_id_arg uuid, profile_id_arg uuid) returns table (
|
||||||
|
id uuid,
|
||||||
|
org_id uuid,
|
||||||
|
title character varying,
|
||||||
|
slug character varying,
|
||||||
|
description character varying,
|
||||||
|
logo text,
|
||||||
|
banner_image text,
|
||||||
|
cost bigint,
|
||||||
|
currency character varying,
|
||||||
|
is_published boolean,
|
||||||
|
total_lessons bigint,
|
||||||
|
total_students bigint,
|
||||||
|
progress_rate bigint,
|
||||||
|
type "COURSE_TYPE",
|
||||||
|
member_profile_id uuid
|
||||||
|
) language plpgsql as $function$
|
||||||
|
BEGIN
|
||||||
|
Return query
|
||||||
|
select course.id, organization.id AS org_id, course.title, course.slug, course.description, course.logo, course.banner_image, course.cost, course.currency, course.is_published, (select COUNT(*) from lesson as l where l.course_id = course.id) AS total_lessons, (select COUNT(*) from groupmember as gm where gm.group_id = course.group_id AND gm.role_id = 3) as total_students, (select COUNT(*) from lesson_completion as lc join lesson as l on l.id = lc.lesson_id where l.course_id = course.id and lc.is_complete = true and lc.profile_id = profile_id_arg) AS progress_rate, course.type as type, (select groupmember.profile_id from groupmember where groupmember.group_id = "group".id and groupmember.profile_id = profile_id_arg) as member_profile_id
|
||||||
|
from course
|
||||||
|
join "group" on "group".id = course.group_id
|
||||||
|
join organization on organization.id = "group".organization_id
|
||||||
|
where course.status = 'ACTIVE' AND organization.id = org_id_arg
|
||||||
|
-- GROUP BY course.id, groupmember.profile_id
|
||||||
|
ORDER BY course.created_at DESC;
|
||||||
|
END;
|
||||||
|
$function$;
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
-- Drop table cause it was never used
|
||||||
|
drop table if exists public.lesson_progress;
|
||||||
|
|
||||||
|
alter table public.exercise
|
||||||
|
drop constraint exercise_lesson_id_fkey,
|
||||||
|
add constraint exercise_lesson_id_fkey foreign key (lesson_id) references lesson (id)
|
||||||
|
on delete cascade;
|
||||||
|
|
||||||
|
alter table public.lesson_comment
|
||||||
|
drop constraint lesson_comment_lesson_id_fkey,
|
||||||
|
add constraint lesson_comment_lesson_id_fkey foreign key (lesson_id) references lesson (id)
|
||||||
|
on delete cascade;
|
||||||
|
|
||||||
|
alter table public.question
|
||||||
|
drop constraint question_exercise_id_fkey,
|
||||||
|
add constraint question_exercise_id_fkey foreign key (exercise_id) references exercise (id)
|
||||||
|
on delete cascade;
|
||||||
|
|
||||||
|
alter table public.option
|
||||||
|
drop constraint option_question_id_fkey,
|
||||||
|
add constraint option_question_id_fkey foreign key (question_id) references question (id)
|
||||||
|
on delete cascade;
|
||||||
|
|
||||||
|
alter table public.question_answer
|
||||||
|
drop constraint question_answer_question_id_fkey,
|
||||||
|
add constraint question_answer_question_id_fkey foreign key (question_id) references question (id)
|
||||||
|
on delete cascade;
|
||||||
|
|
||||||
|
alter table public.submission
|
||||||
|
drop constraint submission_exercise_id_fkey,
|
||||||
|
add constraint submission_exercise_id_fkey foreign key (exercise_id) references exercise (id)
|
||||||
|
on delete cascade;
|
||||||
|
|
||||||
|
alter table public.submission
|
||||||
|
drop constraint submission_course_id_fkey,
|
||||||
|
add constraint submission_course_id_fkey foreign key (course_id) references course (id)
|
||||||
|
on delete cascade;
|
||||||
|
|
||||||
|
alter table public.question_answer
|
||||||
|
drop constraint question_answer_submission_id_fkey,
|
||||||
|
add constraint question_answer_submission_id_fkey foreign key (submission_id) references submission (id)
|
||||||
|
on delete cascade;
|
||||||
|
|
||||||
|
alter table public.lesson_completion
|
||||||
|
drop constraint lesson_completion_lesson_id_fkey,
|
||||||
|
add constraint lesson_completion_lesson_id_fkey foreign key (lesson_id) references lesson (id)
|
||||||
|
on delete cascade;
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
CREATE TABLE lesson_language_history (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
lesson_language_id INTEGER REFERENCES lesson_language(id),
|
||||||
|
old_content TEXT,
|
||||||
|
new_content TEXT,
|
||||||
|
timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE FUNCTION update_lesson_language_history()
|
||||||
|
RETURNS trigger AS $$
|
||||||
|
BEGIN
|
||||||
|
IF TG_OP IN ('INSERT', 'UPDATE') THEN
|
||||||
|
INSERT INTO lesson_language_history (lesson_language_id, old_content, new_content)
|
||||||
|
VALUES (NEW.id, COALESCE(OLD.content, ''), NEW.content);
|
||||||
|
END IF;
|
||||||
|
RETURN NULL;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TRIGGER update_lesson_language_history_trigger
|
||||||
|
AFTER INSERT
|
||||||
|
OR
|
||||||
|
UPDATE ON lesson_language for each row
|
||||||
|
EXECUTE PROCEDURE update_lesson_language_history();
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
create or replace view "public"."lesson_versions" as SELECT llh.old_content,
|
||||||
|
llh.new_content,
|
||||||
|
llh."timestamp",
|
||||||
|
ll.locale,
|
||||||
|
ll.lesson_id
|
||||||
|
FROM (lesson_language_history llh
|
||||||
|
JOIN lesson_language ll ON ((ll.id = llh.lesson_language_id)));
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
CREATE OR REPLACE FUNCTION public.get_explore_courses(org_id_arg uuid, profile_id_arg uuid)
|
||||||
|
RETURNS TABLE(id uuid, org_id uuid, title character varying, slug character varying, description character varying, logo text, banner_image text, cost bigint, currency character varying, is_published boolean, total_lessons bigint, total_students bigint, progress_rate bigint, type "COURSE_TYPE", other_profile_id uuid)
|
||||||
|
LANGUAGE plpgsql
|
||||||
|
AS $function$
|
||||||
|
BEGIN
|
||||||
|
Return query
|
||||||
|
select course.id, organization.id AS org_id, course.title, course.slug, course.description, course.logo, course.banner_image, course.cost, course.currency, course.is_published, (select COUNT(*) from lesson
|
||||||
|
as l where l.course_id = course.id) AS total_lessons, (select COUNT(*) from groupmember as gm where gm.group_id = course.group_id AND gm.role_id = 3) as total_students, (select COUNT(*) from lesson_completion as lc join lesson as l on l.id = lc.lesson_id where l.course_id = course.id and lc.is_complete = true and lc.profile_id = profile_id_arg) AS progress_rate, course.type as type, (select groupmember.profile_id from groupmember where groupmember.group_id = "group".id and groupmember.profile_id != profile_id_arg
|
||||||
|
limit 1) as other_profile_id
|
||||||
|
from course
|
||||||
|
join "group" on "group".id = course.group_id
|
||||||
|
join organization on organization.id = "group".organization_id
|
||||||
|
where course.status = 'ACTIVE' AND course.is_published = true AND organization.id = org_id_arg AND profile_id_arg NOT IN (SELECT groupmember.profile_id FROM groupmember WHERE groupmember.group_id = course.group_id)
|
||||||
|
ORDER BY course.created_at DESC;
|
||||||
|
END;
|
||||||
|
$function$
|
||||||
|
;
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,8 @@
|
|||||||
|
drop policy "User must be an admin to INSERT" on "public"."organization";
|
||||||
|
|
||||||
|
create policy "Enable insert for authenticated users only"
|
||||||
|
on "public"."organization"
|
||||||
|
as permissive
|
||||||
|
for insert
|
||||||
|
to authenticated
|
||||||
|
with check (true);
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
create policy "User must be an org member to DELETE"
|
||||||
|
on "public"."lesson_language_history"
|
||||||
|
as permissive
|
||||||
|
for delete
|
||||||
|
to public
|
||||||
|
using (is_user_in_group_with_role(( SELECT course.group_id
|
||||||
|
FROM course
|
||||||
|
WHERE (course.id = ( SELECT lesson.course_id
|
||||||
|
FROM lesson
|
||||||
|
WHERE (lesson.id = ( SELECT lesson_language.lesson_id
|
||||||
|
FROM lesson_language
|
||||||
|
WHERE (lesson_language.id = lesson_language_history.lesson_language_id)))
|
||||||
|
LIMIT 1))
|
||||||
|
LIMIT 1)));
|
||||||
|
|
||||||
|
|
||||||
|
create policy "User must be an org member to INSERT"
|
||||||
|
on "public"."lesson_language_history"
|
||||||
|
as permissive
|
||||||
|
for insert
|
||||||
|
to public
|
||||||
|
with check (is_user_in_group_with_role(( SELECT course.group_id
|
||||||
|
FROM course
|
||||||
|
WHERE (course.id = ( SELECT lesson.course_id
|
||||||
|
FROM lesson
|
||||||
|
WHERE (lesson.id = ( SELECT lesson_language.lesson_id
|
||||||
|
FROM lesson_language
|
||||||
|
WHERE (lesson_language.id = lesson_language_history.lesson_language_id)))
|
||||||
|
LIMIT 1))
|
||||||
|
LIMIT 1)));
|
||||||
|
|
||||||
|
|
||||||
|
create policy "User must be an org member to UPDATE"
|
||||||
|
on "public"."lesson_language_history"
|
||||||
|
as permissive
|
||||||
|
for update
|
||||||
|
to public
|
||||||
|
using (is_user_in_group_with_role(( SELECT course.group_id
|
||||||
|
FROM course
|
||||||
|
WHERE (course.id = ( SELECT lesson.course_id
|
||||||
|
FROM lesson
|
||||||
|
WHERE (lesson.id = ( SELECT lesson_language.lesson_id
|
||||||
|
FROM lesson_language
|
||||||
|
WHERE (lesson_language.id = lesson_language_history.lesson_language_id)))
|
||||||
|
LIMIT 1))
|
||||||
|
LIMIT 1)))
|
||||||
|
with check (is_user_in_group_with_role(( SELECT course.group_id
|
||||||
|
FROM course
|
||||||
|
WHERE (course.id = ( SELECT lesson.course_id
|
||||||
|
FROM lesson
|
||||||
|
WHERE (lesson.id = ( SELECT lesson_language.lesson_id
|
||||||
|
FROM lesson_language
|
||||||
|
WHERE (lesson_language.id = lesson_language_history.lesson_language_id)))
|
||||||
|
LIMIT 1))
|
||||||
|
LIMIT 1)));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,145 @@
|
|||||||
|
create type "public"."COURSE_VERSION" as enum ('V1', 'V2');
|
||||||
|
|
||||||
|
alter table "public"."lesson_language_history" drop constraint "lesson_language_history_lesson_language_id_fkey";
|
||||||
|
|
||||||
|
alter table "public"."lesson_language" drop constraint "public_lesson_language_lesson_id_fkey";
|
||||||
|
|
||||||
|
create table "public"."lesson_section" (
|
||||||
|
"id" uuid not null default gen_random_uuid(),
|
||||||
|
"created_at" timestamp with time zone not null default now(),
|
||||||
|
"updated_at" timestamp with time zone default now(),
|
||||||
|
"title" character varying,
|
||||||
|
"order" bigint default '0'::bigint,
|
||||||
|
"course_id" uuid
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
alter table "public"."lesson_section" enable row level security;
|
||||||
|
|
||||||
|
alter table "public"."course" add column "version" "COURSE_VERSION" not null default 'V1'::"COURSE_VERSION";
|
||||||
|
|
||||||
|
alter table "public"."lesson" add column "section_id" uuid;
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX lesson_section_pkey ON public.lesson_section USING btree (id);
|
||||||
|
|
||||||
|
alter table "public"."lesson_section" add constraint "lesson_section_pkey" PRIMARY KEY using index "lesson_section_pkey";
|
||||||
|
|
||||||
|
alter table "public"."lesson" add constraint "public_lesson_section_id_fkey" FOREIGN KEY (section_id) REFERENCES lesson_section(id) ON UPDATE CASCADE ON DELETE CASCADE not valid;
|
||||||
|
|
||||||
|
alter table "public"."lesson" validate constraint "public_lesson_section_id_fkey";
|
||||||
|
|
||||||
|
alter table "public"."lesson_language_history" add constraint "public_lesson_language_history_lesson_language_id_fkey" FOREIGN KEY (lesson_language_id) REFERENCES lesson_language(id) ON UPDATE CASCADE ON DELETE CASCADE not valid;
|
||||||
|
|
||||||
|
alter table "public"."lesson_language_history" validate constraint "public_lesson_language_history_lesson_language_id_fkey";
|
||||||
|
|
||||||
|
alter table "public"."lesson_section" add constraint "public_lesson_section_course_id_fkey" FOREIGN KEY (course_id) REFERENCES course(id) ON UPDATE CASCADE ON DELETE CASCADE not valid;
|
||||||
|
|
||||||
|
alter table "public"."lesson_section" validate constraint "public_lesson_section_course_id_fkey";
|
||||||
|
|
||||||
|
alter table "public"."lesson_language" add constraint "public_lesson_language_lesson_id_fkey" FOREIGN KEY (lesson_id) REFERENCES lesson(id) ON UPDATE CASCADE ON DELETE CASCADE not valid;
|
||||||
|
|
||||||
|
alter table "public"."lesson_language" validate constraint "public_lesson_language_lesson_id_fkey";
|
||||||
|
|
||||||
|
grant delete on table "public"."lesson_section" to "anon";
|
||||||
|
|
||||||
|
grant insert on table "public"."lesson_section" to "anon";
|
||||||
|
|
||||||
|
grant references on table "public"."lesson_section" to "anon";
|
||||||
|
|
||||||
|
grant select on table "public"."lesson_section" to "anon";
|
||||||
|
|
||||||
|
grant trigger on table "public"."lesson_section" to "anon";
|
||||||
|
|
||||||
|
grant truncate on table "public"."lesson_section" to "anon";
|
||||||
|
|
||||||
|
grant update on table "public"."lesson_section" to "anon";
|
||||||
|
|
||||||
|
grant delete on table "public"."lesson_section" to "authenticated";
|
||||||
|
|
||||||
|
grant insert on table "public"."lesson_section" to "authenticated";
|
||||||
|
|
||||||
|
grant references on table "public"."lesson_section" to "authenticated";
|
||||||
|
|
||||||
|
grant select on table "public"."lesson_section" to "authenticated";
|
||||||
|
|
||||||
|
grant trigger on table "public"."lesson_section" to "authenticated";
|
||||||
|
|
||||||
|
grant truncate on table "public"."lesson_section" to "authenticated";
|
||||||
|
|
||||||
|
grant update on table "public"."lesson_section" to "authenticated";
|
||||||
|
|
||||||
|
grant delete on table "public"."lesson_section" to "service_role";
|
||||||
|
|
||||||
|
grant insert on table "public"."lesson_section" to "service_role";
|
||||||
|
|
||||||
|
grant references on table "public"."lesson_section" to "service_role";
|
||||||
|
|
||||||
|
grant select on table "public"."lesson_section" to "service_role";
|
||||||
|
|
||||||
|
grant trigger on table "public"."lesson_section" to "service_role";
|
||||||
|
|
||||||
|
grant truncate on table "public"."lesson_section" to "service_role";
|
||||||
|
|
||||||
|
grant update on table "public"."lesson_section" to "service_role";
|
||||||
|
|
||||||
|
create policy "Enable read access for all users"
|
||||||
|
on "public"."lesson_section"
|
||||||
|
as permissive
|
||||||
|
for select
|
||||||
|
to public
|
||||||
|
using (true);
|
||||||
|
|
||||||
|
|
||||||
|
create policy "User must be an org member to DELETE"
|
||||||
|
on "public"."lesson_section"
|
||||||
|
as permissive
|
||||||
|
for delete
|
||||||
|
to public
|
||||||
|
using (is_user_in_group_with_role(( SELECT course.group_id
|
||||||
|
FROM course
|
||||||
|
WHERE (course.id = lesson_section.course_id)
|
||||||
|
LIMIT 1)));
|
||||||
|
|
||||||
|
|
||||||
|
create policy "User must be an org member to INSERT"
|
||||||
|
on "public"."lesson_section"
|
||||||
|
as permissive
|
||||||
|
for insert
|
||||||
|
to public
|
||||||
|
with check (is_user_in_group_with_role(( SELECT course.group_id
|
||||||
|
FROM course
|
||||||
|
WHERE (course.id = lesson_section.course_id)
|
||||||
|
LIMIT 1)));
|
||||||
|
|
||||||
|
|
||||||
|
create policy "User must be an org member to UPDATE"
|
||||||
|
on "public"."lesson_section"
|
||||||
|
as permissive
|
||||||
|
for update
|
||||||
|
to public
|
||||||
|
using (is_user_in_group_with_role(( SELECT course.group_id
|
||||||
|
FROM course
|
||||||
|
WHERE (course.id = lesson_section.course_id)
|
||||||
|
LIMIT 1)))
|
||||||
|
with check (is_user_in_group_with_role(( SELECT course.group_id
|
||||||
|
FROM course
|
||||||
|
WHERE (course.id = lesson_section.course_id)
|
||||||
|
LIMIT 1)));
|
||||||
|
|
||||||
|
CREATE
|
||||||
|
OR REPLACE FUNCTION convert_course_to_v2 (course_id uuid) RETURNS void AS $$
|
||||||
|
DECLARE
|
||||||
|
new_section_id bigint;
|
||||||
|
BEGIN
|
||||||
|
UPDATE course
|
||||||
|
SET version = 'V2'
|
||||||
|
WHERE id = course_id;
|
||||||
|
|
||||||
|
INSERT INTO lesson_section (title, course_id) VALUES ('First Section [edit me]', course_id) RETURNING id INTO new_section_id;
|
||||||
|
|
||||||
|
UPDATE lesson
|
||||||
|
SET section_id = new_section_id
|
||||||
|
WHERE course_id = course_id;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
drop function if exists "public"."convert_course_to_v2"(course_id uuid);
|
||||||
|
|
||||||
|
alter table "public"."organization" add column "is_restricted" boolean not null default false;
|
||||||
|
|
||||||
|
alter table "public"."profile" add column "is_restricted" boolean not null default false;
|
||||||
|
|
||||||
|
set check_function_bodies = off;
|
||||||
|
|
||||||
|
CREATE
|
||||||
|
OR REPLACE FUNCTION convert_course_to_v2 (course_id_arg uuid) RETURNS void AS $$
|
||||||
|
DECLARE
|
||||||
|
new_section_id uuid;
|
||||||
|
BEGIN
|
||||||
|
UPDATE course
|
||||||
|
SET version = 'V2'
|
||||||
|
WHERE id = course_id_arg;
|
||||||
|
|
||||||
|
INSERT INTO lesson_section (title, course_id) VALUES ('First Section [edit me]', course_id_arg) RETURNING id INTO new_section_id;
|
||||||
|
|
||||||
|
UPDATE lesson
|
||||||
|
SET section_id = new_section_id
|
||||||
|
WHERE lesson.course_id = course_id_arg;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
alter table "public"."organization" add column "customCode" text;
|
||||||
|
|
||||||
|
alter table "public"."organization" add column "customDomain" text;
|
||||||
|
|
||||||
|
alter table "public"."organization" add column "favicon" text;
|
||||||
|
|
||||||
|
alter table "public"."organization" add column "isCustomDomainVerified" boolean default false;
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX "organization_customDomain_key" ON public.organization USING btree ("customDomain");
|
||||||
|
|
||||||
|
alter table "public"."organization" add constraint "organization_customDomain_key" UNIQUE using index "organization_customDomain_key";
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
drop policy "User must be an org member to SELECT" on "public"."organization_plan";
|
||||||
|
|
||||||
|
create policy "Enable read access for all users"
|
||||||
|
on "public"."organization_plan"
|
||||||
|
as permissive
|
||||||
|
for select
|
||||||
|
to public
|
||||||
|
using (true);
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
create table "public"."analytics_login_events" (
|
||||||
|
"id" uuid not null default uuid_generate_v4(),
|
||||||
|
"user_id" uuid not null,
|
||||||
|
"logged_in_at" timestamp with time zone default now()
|
||||||
|
);
|
||||||
|
|
||||||
|
alter table "public"."analytics_login_events" enable row level security;
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX analytics_login_events_pkey ON public.analytics_login_events USING btree (id);
|
||||||
|
|
||||||
|
CREATE INDEX idx_analytics_login_events_logged_in_at ON public.analytics_login_events USING btree (logged_in_at);
|
||||||
|
|
||||||
|
CREATE INDEX idx_analytics_login_events_user_id ON public.analytics_login_events USING btree (user_id);
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX analytics_login_events_user_id_unique ON public.analytics_login_events USING btree (user_id);
|
||||||
|
|
||||||
|
alter table "public"."analytics_login_events" add constraint "analytics_login_events_pkey" PRIMARY KEY using index "analytics_login_events_pkey";
|
||||||
|
|
||||||
|
alter table "public"."analytics_login_events" add constraint "analytics_login_events_user_id_fkey" FOREIGN KEY (user_id) REFERENCES auth.users(id) not valid;
|
||||||
|
|
||||||
|
alter table "public"."analytics_login_events" validate constraint "analytics_login_events_user_id_fkey";
|
||||||
|
|
||||||
|
alter table "public"."analytics_login_events" add constraint "analytics_login_events_user_id_unique" UNIQUE using index "analytics_login_events_user_id_unique";
|
||||||
|
|
||||||
|
set check_function_bodies = off;
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION public.insert_login_event_on_user_login()
|
||||||
|
RETURNS trigger
|
||||||
|
LANGUAGE plpgsql
|
||||||
|
SECURITY DEFINER
|
||||||
|
AS $function$
|
||||||
|
BEGIN
|
||||||
|
IF (NEW.last_sign_in_at IS NOT NULL) THEN
|
||||||
|
INSERT INTO public.analytics_login_events (logged_in_at, user_id)
|
||||||
|
VALUES (NEW.last_sign_in_at, NEW.id)
|
||||||
|
ON CONFLICT (user_id) DO UPDATE
|
||||||
|
SET logged_in_at = NEW.last_sign_in_at;
|
||||||
|
END IF;
|
||||||
|
RETURN NEW;
|
||||||
|
END;
|
||||||
|
$function$
|
||||||
|
;
|
||||||
|
|
||||||
|
CREATE TRIGGER insert_login_event_on_user_login_trigger
|
||||||
|
AFTER UPDATE OF last_sign_in_at ON auth.users
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE FUNCTION public.insert_login_event_on_user_login();
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION public.insert_login_event_on_user_session_update()
|
||||||
|
RETURNS trigger
|
||||||
|
LANGUAGE plpgsql
|
||||||
|
SECURITY DEFINER
|
||||||
|
AS $function$
|
||||||
|
BEGIN
|
||||||
|
IF (NEW.updated_at IS NOT NULL AND NEW.updated_at != OLD.updated_at) THEN
|
||||||
|
INSERT INTO public.analytics_login_events (logged_in_at, user_id)
|
||||||
|
VALUES (NEW.updated_at, NEW.user_id)
|
||||||
|
ON CONFLICT (user_id) DO UPDATE
|
||||||
|
SET logged_in_at = EXCLUDED.logged_in_at
|
||||||
|
WHERE analytics_login_events.logged_in_at < EXCLUDED.logged_in_at;
|
||||||
|
END IF;
|
||||||
|
RETURN NEW;
|
||||||
|
END;
|
||||||
|
$function$
|
||||||
|
;
|
||||||
|
|
||||||
|
CREATE TRIGGER insert_login_event_on_user_session_update_trigger
|
||||||
|
AFTER UPDATE OF updated_at ON auth.sessions
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE FUNCTION public.insert_login_event_on_user_session_update();
|
||||||
|
|
||||||
|
grant delete on table "public"."analytics_login_events" to "anon";
|
||||||
|
|
||||||
|
grant insert on table "public"."analytics_login_events" to "anon";
|
||||||
|
|
||||||
|
grant references on table "public"."analytics_login_events" to "anon";
|
||||||
|
|
||||||
|
grant select on table "public"."analytics_login_events" to "anon";
|
||||||
|
|
||||||
|
grant trigger on table "public"."analytics_login_events" to "anon";
|
||||||
|
|
||||||
|
grant truncate on table "public"."analytics_login_events" to "anon";
|
||||||
|
|
||||||
|
grant update on table "public"."analytics_login_events" to "anon";
|
||||||
|
|
||||||
|
grant delete on table "public"."analytics_login_events" to "authenticated";
|
||||||
|
|
||||||
|
grant insert on table "public"."analytics_login_events" to "authenticated";
|
||||||
|
|
||||||
|
grant references on table "public"."analytics_login_events" to "authenticated";
|
||||||
|
|
||||||
|
grant select on table "public"."analytics_login_events" to "authenticated";
|
||||||
|
|
||||||
|
grant trigger on table "public"."analytics_login_events" to "authenticated";
|
||||||
|
|
||||||
|
grant truncate on table "public"."analytics_login_events" to "authenticated";
|
||||||
|
|
||||||
|
grant update on table "public"."analytics_login_events" to "authenticated";
|
||||||
|
|
||||||
|
grant delete on table "public"."analytics_login_events" to "service_role";
|
||||||
|
|
||||||
|
grant insert on table "public"."analytics_login_events" to "service_role";
|
||||||
|
|
||||||
|
grant references on table "public"."analytics_login_events" to "service_role";
|
||||||
|
|
||||||
|
grant select on table "public"."analytics_login_events" to "service_role";
|
||||||
|
|
||||||
|
grant trigger on table "public"."analytics_login_events" to "service_role";
|
||||||
|
|
||||||
|
grant truncate on table "public"."analytics_login_events" to "service_role";
|
||||||
|
|
||||||
|
grant update on table "public"."analytics_login_events" to "service_role";
|
||||||
|
|
||||||
|
create policy "Users can insert their own login events"
|
||||||
|
on "public"."analytics_login_events"
|
||||||
|
as permissive
|
||||||
|
for insert
|
||||||
|
to authenticated
|
||||||
|
with check ((auth.uid() = user_id));
|
||||||
|
|
||||||
|
CREATE POLICY "Enable delete for users based on user_id"
|
||||||
|
ON "public"."analytics_login_events"
|
||||||
|
AS PERMISSIVE FOR DELETE
|
||||||
|
TO public
|
||||||
|
USING (auth.uid() = user_id);
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
set check_function_bodies = off;
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION public.check_if_student_completed_exercises(lesson_id_arg uuid, groupmember_id_arg uuid)
|
||||||
|
RETURNS boolean
|
||||||
|
LANGUAGE plpgsql
|
||||||
|
AS $function$
|
||||||
|
DECLARE
|
||||||
|
all_exercises_exist boolean;
|
||||||
|
BEGIN
|
||||||
|
SELECT COUNT(*) = COUNT(s.id) INTO all_exercises_exist
|
||||||
|
FROM exercise e
|
||||||
|
LEFT JOIN submission s ON e.id = s.exercise_id AND s.submitted_by = groupmember_id_arg
|
||||||
|
WHERE e.lesson_id = lesson_id_arg;
|
||||||
|
|
||||||
|
RETURN all_exercises_exist;
|
||||||
|
END;
|
||||||
|
$function$
|
||||||
|
;
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
set check_function_bodies = off;
|
||||||
|
|
||||||
|
create or replace view "public"."dash_org_stats" as SELECT gp.organization_id AS org_id,
|
||||||
|
count(DISTINCT course.id) AS no_of_courses,
|
||||||
|
count(DISTINCT gm.profile_id) AS enrolled_students
|
||||||
|
FROM ((course
|
||||||
|
JOIN "group" gp ON ((gp.id = course.group_id)))
|
||||||
|
LEFT JOIN groupmember gm ON (((gm.group_id = gp.id) AND (gm.role_id = 3))))
|
||||||
|
WHERE (course.status = 'ACTIVE'::text)
|
||||||
|
GROUP BY gp.organization_id;
|
||||||
|
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION public.get_dash_org_recent_enrollments(org_id_arg uuid)
|
||||||
|
RETURNS TABLE(profile_id uuid, avatar_url text, fullname text, course_id uuid, course_title character varying, enrolled_at timestamp with time zone)
|
||||||
|
LANGUAGE plpgsql
|
||||||
|
AS $function$
|
||||||
|
BEGIN
|
||||||
|
RETURN QUERY
|
||||||
|
SELECT
|
||||||
|
p.id,
|
||||||
|
p.avatar_url,
|
||||||
|
p.fullname,
|
||||||
|
course.id,
|
||||||
|
course.title,
|
||||||
|
gm.created_at as enrolled_at
|
||||||
|
FROM
|
||||||
|
course
|
||||||
|
JOIN "group" as gp ON gp.id = course.group_id
|
||||||
|
LEFT JOIN groupmember as gm ON gm.group_id = gp.id AND gm.role_id = 3
|
||||||
|
JOIN profile as p ON p.id = gm.profile_id
|
||||||
|
WHERE
|
||||||
|
course.status = 'ACTIVE'
|
||||||
|
AND gp.organization_id = org_id_arg
|
||||||
|
GROUP BY
|
||||||
|
p.id,
|
||||||
|
course.id,
|
||||||
|
course.title,
|
||||||
|
enrolled_at
|
||||||
|
ORDER BY
|
||||||
|
enrolled_at DESC
|
||||||
|
LIMIT 5;
|
||||||
|
END;
|
||||||
|
$function$
|
||||||
|
;
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION public.get_dash_org_top_courses(org_id_arg uuid)
|
||||||
|
RETURNS TABLE(course_id uuid, course_title character varying, total_students integer, completion_percentage integer)
|
||||||
|
LANGUAGE plpgsql
|
||||||
|
AS $function$
|
||||||
|
BEGIN
|
||||||
|
RETURN QUERY
|
||||||
|
SELECT
|
||||||
|
course_stats.course_id,
|
||||||
|
course_stats.course_title,
|
||||||
|
course_stats.total_students::integer,
|
||||||
|
CASE
|
||||||
|
WHEN course_stats.total_students * course_stats.total_lessons = 0 THEN 0
|
||||||
|
ELSE ROUND((course_stats.completed_lessons::numeric / (course_stats.total_students * course_stats.total_lessons)) * 100)::integer
|
||||||
|
END as completion_percentage
|
||||||
|
FROM (
|
||||||
|
SELECT
|
||||||
|
c.id as course_id,
|
||||||
|
c.title as course_title,
|
||||||
|
COUNT(DISTINCT gm.id) as total_students,
|
||||||
|
COUNT(DISTINCT l.id) as total_lessons,
|
||||||
|
COUNT(DISTINCT lc.id) FILTER (WHERE lc.is_complete = true) as completed_lessons
|
||||||
|
FROM course c
|
||||||
|
JOIN "group" g ON g.id = c.group_id
|
||||||
|
LEFT JOIN groupmember gm ON gm.group_id = g.id and gm.role_id = 3
|
||||||
|
LEFT JOIN lesson l ON l.course_id = c.id
|
||||||
|
LEFT JOIN lesson_completion lc ON lc.lesson_id = l.id
|
||||||
|
AND lc.profile_id = gm.profile_id
|
||||||
|
WHERE c.status = 'ACTIVE' AND g.organization_id = org_id_arg
|
||||||
|
GROUP BY c.id, c.title
|
||||||
|
ORDER BY completed_lessons DESC
|
||||||
|
) as course_stats
|
||||||
|
LIMIT 5;
|
||||||
|
END;
|
||||||
|
$function$
|
||||||
|
;
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,7 @@
|
|||||||
|
alter table "public"."organization_plan" add column "provider" text default 'lmz'::text;
|
||||||
|
|
||||||
|
alter table "public"."organization_plan" add column "subscription_id" text;
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX organization_plan_subscription_id_key ON public.organization_plan USING btree (subscription_id);
|
||||||
|
|
||||||
|
alter table "public"."organization_plan" add constraint "organization_plan_subscription_id_key" UNIQUE using index "organization_plan_subscription_id_key";
|
||||||
Reference in New Issue
Block a user