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
- Module Structure
- Lesson Plan Endpoints
- Core Features
- Lesson Plan Lifecycle
- Progress Tracking System
- Assignment System
- Subscription Integration
- Database Functions & Triggers
- Hasura GraphQL Functions
- Data Models
- 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
| Endpoint | Method | Description | Auth Required | Roles |
|---|---|---|---|---|
/get-lesson-plans-by-child | POST | Fetch lesson plans for child | JWT | All Roles |
/get-lesson-plan-details | POST | Get specific lesson plan details | JWT | All Roles |
/get-linked-lesson-plans | POST | Get linked lesson plans | JWT | PARENT |
GraphQL Function Operations
| Function | Method | Description | Auth Required | Roles |
|---|---|---|---|---|
assign_lesson_plans_to_child | Mutation | Assign lesson plans to child | JWT | ADMIN, THERAPIST |
capture_lesson_plan_progress | Mutation | Update progress tracking | JWT | PARENT |
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
| Role | Assignment Method | Restrictions | Source Attribution |
|---|---|---|---|
| ADMIN | Database function | None | ADMIN |
| THERAPIST | Database function | Child mapping required | THERAPIST |
| SYSTEM | Auto-assignment | Assessment-triggered | GENERATED |
| PARENT | Subscription access | Feature-gated | SUBSCRIPTION |
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 contextinput_child_id: Target child UUIDinput_lesson_plan_ids: JSON array of lesson plan UUIDsinput_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:
- Session validation - Extract and verify user credentials
- Input validation - Check UUID format and required fields
- Child validation - Verify child exists and is active
- Lesson plan validation - Check existence, activity status, and age compatibility
- Duplication prevention - Ensure lesson plan not already assigned
- Assignment creation - Create progress records with proper ordering
- 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 contextinput_child_id: Child UUIDinput_lesson_plan_id: Lesson plan UUIDinput_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.