Skip to main content

Lesson Plan Module

The Lesson Plan module is a comprehensive learning management system for the Comdeall platform that manages developmental lesson plans and their assignment to children. It handles subscription-based access control, age-appropriate filtering, progress tracking, automatic assignment workflows, and multi-role management for parents, therapists, and administrators to deliver targeted interventions for child development.

Table of Contents​

  1. Module Structure
  2. Lesson Plan Endpoints
  3. Core Features
  4. Lesson Plan Lifecycle
  5. Progress Tracking System
  6. Assignment System
  7. Subscription Integration
  8. Database Functions & Triggers
  9. Hasura GraphQL Functions
  10. Data Models
  11. Security & Validation

Module Structure​

The Lesson Plan module follows a layered architecture pattern:

@Module({
imports: [DBModule],
controllers: [LessonPlanController],
providers: [LessonPlanService, LessonPlanDBService, JwtService],
})
export class LessonPlanModule {}

Components:

  • LessonPlanController: API endpoints and request handling
  • LessonPlanService: Business logic and subscription validation
  • LessonPlanDBService: Database abstraction layer
  • LessonPlanDBRepository: Data access and complex queries
  • Database Functions: PostgreSQL functions for assignments and progress tracking

Lesson Plan Endpoints​

Core Lesson Plan Operations​

EndpointMethodDescriptionAuth RequiredRoles
/get-lesson-plans-by-childPOSTFetch lesson plans for childJWTAll Roles
/get-lesson-plan-detailsPOSTGet specific lesson plan detailsJWTAll Roles
/get-linked-lesson-plansPOSTGet linked lesson plansJWTPARENT

GraphQL Function Operations​

FunctionMethodDescriptionAuth RequiredRoles
assign_lesson_plans_to_childMutationAssign lesson plans to childJWTADMIN, THERAPIST
capture_lesson_plan_progressMutationUpdate progress trackingJWTPARENT

Core Features​

1. Subscription-Based Access Control​

  • Free plan inclusion - Always includes free lesson plans
  • Paid subscription filtering - Access based on active subscription plans
  • Feature gating - Controls linked lesson plan access via subscription features
  • Multi-plan support - Combines multiple subscription plans seamlessly

2. Age-Appropriate Filtering​

// Age calculation and filtering
const childAge = getAgeInMonths(child.dob);
const ageCompatible = childAge >= lessonPlan.min_age &&
childAge <= lessonPlan.max_age;

3. Domain-Based Categorization​

enum LessonType {
GROSS_MOTOR = 'GROSS_MOTOR',
FINE_MOTOR = 'FINE_MOTOR',
ACTIVITIES_OF_DAILY_LIVING = 'ACTIVITIES_OF_DAILY_LIVING',
RECEPTIVE_LANGUAGE = 'RECEPTIVE_LANGUAGE',
EXPRESSIVE_LANGUAGE = 'EXPRESSIVE_LANGUAGE',
COGNITIVE_SKILLS = 'COGNITIVE_SKILLS',
SOCIAL_SKILLS = 'SOCIAL_SKILLS',
EMOTIONAL_SKILLS = 'EMOTIONAL_SKILLS',
PREREQUISITES_OF_LEARNING = 'PREREQUISITES_OF_LEARNING',
ORO_MOTOR = 'ORO_MOTOR',
OTHERS = 'OTHERS'
}

4. Smart Filtering System​

  • Specialty matching for therapist-specific lesson plans
  • Language filtering for multilingual support
  • Status-based filtering (assigned vs not assigned)
  • Age range filtering with flexible criteria

5. Progress Tracking & Management​

  • Activity completion tracking with percentage calculations
  • Status progression (NOT_STARTED → IN_PROGRESS → COMPLETED)
  • Unlock mechanisms for sequential lesson plan access
  • Source attribution (SUBSCRIPTION, LINKED, ADMIN, THERAPIST, GENERATED)

Lesson Plan Lifecycle​

1. Discovery Phase​

// Subscription-based filtering
1. Get child's active subscriptions
2. Include free plan automatically
3. Filter by age compatibility
4. Apply domain/specialty filters
5. Check user authorization

2. Assignment Phase​

// Multi-source assignment support
1. Manual assignment (Admin/Therapist)
2. Subscription-based access (automatic)
3. Assessment-linked assignment (automated)
4. Progress tracking initialization

3. Progress Phase​

// Comprehensive progress tracking
1. Activity completion monitoring
2. Percentage calculation updates
3. Status progression management
4. Unlock condition evaluation

4. Completion Phase​

// Completion workflow
1. Final activity completion
2. Status update to COMPLETED
3. Next lesson plan unlock
4. Parent notification dispatch

Progress Tracking System​

Progress Status Management​

enum ChildLessonPlanProgressStatus {
NOT_STARTED = 'NOT_STARTED',
IN_PROGRESS = 'IN_PROGRESS',
COMPLETED = 'COMPLETED'
}

enum ChildLessonPlanProgressSource {
LINKED = 'LINKED', // From assessment results
ADMIN = 'ADMIN', // Manual admin assignment
THERAPIST = 'THERAPIST', // Manual therapist assignment
SUBSCRIPTION = 'SUBSCRIPTION', // Subscription access
}

Progress Calculation Logic​

// Automatic progress percentage calculation
percentage_completed = (completed_activities / total_activities) * 100;

// Status determination
status = completed_activities === 0 ? 'NOT_STARTED' :
completed_activities === total_activities ? 'COMPLETED' :
'IN_PROGRESS';

Default Progress Assignment​

// For lesson plans without progress records
if (!lessonPlan.child_lesson_plan_progress.length) {
lessonPlan.child_lesson_plan_progress.push({
child_id: childId,
lesson_plan_id: lessonPlan.id,
status: ChildLessonPlanProgressStatus.NOT_STARTED,
source: ChildLessonPlanProgressSource.SUBSCRIPTION,
total_activities: lessonPlan.activity_count,
completed_activities: 0,
percentage_completed: 0
});
}

Assignment System​

Multi-Role Assignment Support​

RoleAssignment MethodRestrictionsSource Attribution
ADMINDatabase functionNoneADMIN
THERAPISTDatabase functionChild mapping requiredTHERAPIST
SYSTEMAuto-assignmentAssessment-triggeredGENERATED
PARENTSubscription accessFeature-gatedSUBSCRIPTION

Authorization Matrix​

// Parent access validation
if (userRole === UserRole.PARENT &&
subscriptionDetailsByChildId.parent.user_id !== userId) {
throw new BadRequestException('Not authorized to access this child');
}

// Therapist access validation
if (userRole === UserRole.THERAPIST && !therapist_id) {
throw new BadRequestException('Therapist ID is required');
}

Subscription Feature Validation​

// Linked lesson plan feature check
const hasLinkedFeature = childSubscriptionDetails
.subscription_products.subscription_plans.subscription_plan_features
.some(feature =>
feature.subscription_features.feature_name ===
SubscriptionFeature.LINKED_LESSON_PLAN
);

Subscription Integration​

Free Plan Integration​

// Always include free plan lesson plans
const freePlanIds = freePlanDetails.id;
const allSubscriptionPlanIds = [
...new Set([...subscriptionPlanIds, freePlanIds])
];

Feature-Based Access Control​

enum SubscriptionFeature {
LINKED_LESSON_PLAN = "LINKED_LESSON_PLAN",
LESSON_PLAN_ASSIGNMENT = "lesson_plan_assignment"
}

Access Determination Logic​

// Parent access: subscription + free plans + parent assessor
// Therapist access: specialty-filtered plans
// Admin access: all plans

Database Functions & Triggers​

1. Lesson Plan Activity Validation Triggers​

Function: check_lesson_plan_activity() Trigger: trigger_check_lesson_plan_activity

Purpose: Ensures lesson plans have at least one activity before activation

CREATE TRIGGER trigger_check_lesson_plan_activity
BEFORE INSERT OR UPDATE ON lesson_plan
FOR EACH ROW
EXECUTE FUNCTION check_lesson_plan_activity();

Validation Logic:

  • Counts activities linked to lesson plan
  • Prevents activation if no activities exist
  • Raises exception with detailed error message

Function: deactivate_lesson_plan_if_no_activities() Trigger: trigger_deactivate_lesson_plan

Purpose: Automatically deactivates lesson plans when last activity is deleted

CREATE TRIGGER trigger_deactivate_lesson_plan
AFTER DELETE ON activity
FOR EACH ROW
EXECUTE FUNCTION deactivate_lesson_plan_if_no_activities();

2. Lesson Plan Assignment Function​

Function: assign_lesson_plans_to_child() Type: PostgreSQL function exposed as GraphQL mutation

Parameters:

  • hasura_session: Authentication and authorization context
  • input_child_id: Target child UUID
  • input_lesson_plan_ids: JSON array of lesson plan UUIDs
  • input_source: Optional source override

Comprehensive Authorization Logic:

-- Role-based authorization
IF token_role NOT IN ('ADMIN', 'THERAPIST') THEN
RAISE EXCEPTION 'Unauthorized access. Only ADMIN or THERAPIST can assign';
END IF;

-- Therapist-specific validation
IF token_role = 'THERAPIST' THEN
-- Check child-therapist mapping
-- Verify subscription features
-- Validate therapist session data
END IF;

Validation Workflow:

  1. Session validation - Extract and verify user credentials
  2. Input validation - Check UUID format and required fields
  3. Child validation - Verify child exists and is active
  4. Lesson plan validation - Check existence, activity status, and age compatibility
  5. Duplication prevention - Ensure lesson plan not already assigned
  6. Assignment creation - Create progress records with proper ordering
  7. Notification dispatch - Notify parents of new assignments

Advanced Features:

  • Sequential ordering - Maintains lesson plan sequence with auto-incrementing order
  • Age compatibility - Validates child age against lesson plan age ranges
  • Activity counting - Automatically calculates total activities per lesson plan
  • Unlock logic - First lesson plan is unlocked, others require progression
  • Audit trailing - Tracks assignment source and assigning user

3. Progress Tracking Function​

Function: capture_lesson_plan_progress() Type: PostgreSQL function for parent progress updates

Parameters:

  • hasura_session: Parent authentication context
  • input_child_id: Child UUID
  • input_lesson_plan_id: Lesson plan UUID
  • input_completed_activities: Number of completed activities

Authorization & Validation:

-- Parent-only access
IF token_role != 'PARENT' THEN
RAISE EXCEPTION 'Unauthorized access: Invalid user or role';
END IF;

-- Child ownership validation
SELECT EXISTS(
SELECT 1 FROM child c
INNER JOIN parent p ON c.parent_id = p.id
WHERE c.id = input_child_id AND p.user_id = token_user_id
) INTO v_child_exists;

Progress Calculation Logic:

-- Calculate new percentage
v_new_percentage_completed :=
(input_completed_activities::FLOAT / v_total_activities::FLOAT) * 100.0;

-- Determine status
v_new_status := CASE
WHEN input_completed_activities = 0 THEN 'NOT_STARTED'
WHEN input_completed_activities >= v_total_activities THEN 'COMPLETED'
ELSE 'IN_PROGRESS'
END;

-- Set completion timestamp
v_new_completed_at := CASE
WHEN v_new_status = 'COMPLETED' THEN now()
ELSE NULL
END;

4. Auto-Assignment from Assessment Completion​

Integration: Called from auto_assign_lesson_plans_on_assessment_completion()

Workflow:

-- From assessment completion trigger
IF array_length(v_lesson_plan_ids_to_assign, 1) > 0 THEN
-- Call lesson plan assignment function
SELECT * FROM assign_lesson_plans_to_child(
'{"x-hasura-user-id": "00000000-0000-0000-0000-000000000000",
"x-hasura-role": "ADMIN"}'::JSON,
v_child_id,
v_lesson_plan_ids_json,
'GENERATED'
);
END IF;

5. Update Triggers​

Standard timestamp triggers:

CREATE TRIGGER set_public_child_lesson_progress_updated_at
BEFORE UPDATE ON child_lesson_plan_progress
EXECUTE FUNCTION set_updated_at_column();

Hasura GraphQL Functions​

1. assign_lesson_plans_to_child​

Configuration:

function:
name: assign_lesson_plans_to_child
schema: public
configuration:
exposed_as: mutation
session_argument: hasura_session
permissions:
- role: THERAPIST
- role: ADMIN

Usage:

mutation AssignLessonPlans {
assign_lesson_plans_to_child(
input_child_id: "child-uuid"
input_lesson_plan_ids: ["plan-1", "plan-2"]
input_source: "THERAPIST"
) {
id
child_id
lesson_plan_id
status
source
lesson_plan_order
total_activities
}
}

2. capture_lesson_plan_progress​

Configuration:

function:
name: capture_lesson_plan_progress
schema: public
configuration:
exposed_as: mutation
session_argument: hasura_session
permissions:
- role: PARENT

Usage:

mutation CaptureProgress {
capture_lesson_plan_progress(
input_child_id: "child-uuid"
input_lesson_plan_id: "plan-uuid"
input_completed_activities: 5
) {
id
status
completed_activities
total_activities
percentage_completed
completed_at
}
}

Data Models​

Core Lesson Plan Model​

interface LessonPlanData {
id: string;
lesson_name: string;
lesson_type: LessonType;
language_id: string;
min_age: number;
max_age: number;
is_active: boolean;
activity_count: number;
languages: LessonPlanLanguage;
media: LessonPlanMedia | null;
lesson_plan_subscription: LessonPlanSubscriptionPlan[];
child_lesson_plan_progress: ChildLessonPlanProgress[];
custom_sort_order?: number;
is_unlocked?: boolean;
}

Progress Tracking Model​

interface ChildLessonPlanProgress {
child_id: string;
lesson_plan_id: string;
status: ChildLessonPlanProgressStatus;
source: ChildLessonPlanProgressSource;
lesson_plan_order: number;
total_activities: number;
completed_activities: number;
percentage_completed: number;
completed_at: string | null;
unlocked: boolean;
user_id?: string; // Assigning user
}

Subscription Integration Model​

interface LessonPlanSubscriptionPlan {
id: string;
name: string;
plan_type: string;
}

Filter Models​

interface LessonPlanAgeFilter {
min_age?: number;
max_age?: number;
}

enum LessonPlanListFilterType {
ALL = 'ALL',
NOT_ASSIGNED = 'NOT_ASSIGNED'
}

Security & Validation​

Input Validation​

  • DTO validation using class-validator decorators
  • UUID format validation for all entity references
  • Age range validation (0-216 months supported)
  • Pagination limits (1-100 items per page)

Authorization Patterns​

// Role-based endpoint protection
@Roles(UserRole.PARENT)
@Auth(AuthType.JWT)

// Database function authorization
IF token_role NOT IN ('ADMIN', 'THERAPIST') THEN
RAISE EXCEPTION 'Unauthorized access';
END IF;

Data Access Control​

  • Parent access: Only own children's lesson plans
  • Therapist access: Children mapped to therapist with specialty matching
  • Admin access: All lesson plans with appropriate feature validation
  • Subscription enforcement: Feature-based access control

Database Constraints​

  • Foreign key constraints with CASCADE operations
  • Age compatibility validation in assignment functions
  • Duplicate prevention logic in progress tracking
  • Activity count validation for lesson plan activation

Error Handling​

  • Structured error responses with specific error codes
  • Detailed validation messages for troubleshooting
  • Graceful degradation for missing progress records
  • Transaction rollback on validation failures

Key Implementation Details​

Age Compatibility Logic​

// Age calculation in months
const childAge = getAgeInMonths(child.dob);

// Database validation
IF v_child_age < v_lesson_plan_record.min_age OR
v_child_age > v_lesson_plan_record.max_age THEN
RAISE EXCEPTION 'Child age does not match lesson plan age range';
END IF;

Subscription Plan Merging​

// Combine paid and free plans
const allSubscriptionPlanIds = [
...new Set([...subscriptionPlanIds, freePlanIds])
];

Sequential Ordering​

-- Maintain lesson plan sequence
SELECT COALESCE(MAX(lesson_plan_order), 0) INTO v_max_order
FROM child_lesson_plan_progress WHERE child_id = input_child_id;

v_max_order := v_max_order + 1;

Unlock Mechanism​

-- First lesson plan is unlocked, others require progression
unlocked = CASE WHEN v_current_order = 1 THEN true ELSE false END

The Lesson Plan module provides a comprehensive learning management system with sophisticated assignment workflows, progress tracking, and multi-role access control, enabling personalized developmental interventions for children through the Comdeall platform.


Conclusion​

The Lesson Plan module serves as a sophisticated learning management system that seamlessly integrates subscription management, automated assignment workflows, comprehensive progress tracking, and role-based access control. Through the combination of REST APIs, GraphQL functions, and database triggers, it delivers a robust platform for managing developmental lesson plans and tracking child progress across the Comdeall ecosystem.