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.