BCM - Welcome Meals Exercise Addition
Summary
Successfully added the “BCM - Welcome Meals” exercise to the Data Quality Validation App and created comprehensive documentation for adding new exercises in the future.
Date: October 23, 2025
Status: Complete ✓
What Was Added
1. New Exercise: BCM - Welcome Meals
File: app/exercises/bcm_welcome_meals.r
Configuration:
- Survey ID: 5404 (found via xlsFormId 2168)
- xlsFormId: 2168
- Type: Survey
- Filter: By Enumerator Name
- Description: Jordan BCM welcome meals distribution quality monitoring
Note: The initial placeholder ID was 5400, but the actual survey ID was found to be 5404 by querying the API with xlsFormId 2168. See Troubleshooting Survey ID below for details.
Features:
- Data fetching from DataBridges API
- Standard data preparation (removes problematic columns, converts dates)
- Custom meal type field cleaning
- Comprehensive validation rules:
- Duration check (5 min - 1 hour, adjusted for shorter surveys)
- Same-day submission validation
- Late submission check (within 24 hours)
- Beneficiary ID uniqueness per day
- Phone number uniqueness per day
- Complaint rate monitoring (max 10%)
- Missing data detection (max 30% per row)
- Custom visualization dashboard
2. Custom Visualization Dashboard
File: app/R/visualization_helpers.r
Function: bcm_welcome_meals_dashboard(data)
Dashboard Sections:
- Meal Quality Metrics:
- Good Meal Quality percentage
- Sufficient Quantity percentage
- Appropriate Temperature percentage
- Service Quality:
- Meals Served On Time percentage
- Staff Were Courteous percentage
- Complaint rate (inverted - shows “No complaint” percentage)
- Distribution Details:
- Distribution by Location breakdown
- Meal Type Distribution:
- Breakdown of meal types served
- Beneficiary Demographics:
- Gender Distribution
- Age Group Distribution
3. Comprehensive Developer Documentation
File: docs/ADDING_NEW_EXERCISES.md
Contents:
- Complete step-by-step guide for adding new exercises
- Exercise structure reference
- Validation rules reference with examples
- Visualization components reference
- Testing checklist
- Troubleshooting guide
- Best practices
- Complete working example
Sections:
- Overview of the registry-based system
- Prerequisites for adding exercises
- 10-step process with code examples
- Exercise structure reference
- Validation rules reference (all available rules)
- Visualization reference (all UI components)
- Testing checklist
- Troubleshooting common issues
- Best practices for code quality, validations, and visualizations
- Complete example with annotations
How It Works
Automatic Registration
The exercise is automatically discovered by the app’s registry system:
- Exercise file is placed in
app/exercises/ - Registry (
app/R/exercise_registry.r) scans directory on startup - Exercise appears in app dropdown immediately
- No manual registration required
Data Flow
User selects exercise
↓
fetch_data() retrieves data from API
↓
prepare_data() cleans and transforms data
↓
User applies filters (optional)
↓
Validation rules execute
↓
Results + Visualizations display
Troubleshooting Survey ID
The Problem
When initially creating this exercise, we used a placeholder survey ID (5400). When testing the app:
ERROR fetching from BCM - Welcome Meals (Survey 5400):
TypeError: object of type 'NoneType' has no len()
This error meant survey ID 5400 didn’t exist or wasn’t accessible.
The Solution
We had the xlsFormId (2168) but needed to find the corresponding surveyID.
Step 1: Get all surveys and find the mapping
library(reticulate)
library(dplyr)
# Setup API client
use_python(".venv/bin/python")
data_bridges_knots <- import("data_bridges_knots")
client <- data_bridges_knots$DataBridgesShapes("data_bridges_api_config.yaml")
# Get all surveys
surveys <- client$get_household_surveys_list(page = 1L)
# View the relevant columns
surveys |>
select(surveyID, xlsFormId, xlsFormName, baseXlsFormId, baseXlsFormName, iso3Alpha3)Step 2: Filter by xlsFormId
> surveys |>
filter(xlsFormId == 2168) |>
select(surveyID, xlsFormId, xlsFormName)
surveyID xlsFormId xlsFormName
5404 2168 BCM - Welcome meals - 20251021Found it! The correct survey ID is 5404, not 5400.
Step 3: Update the exercise file
Updated three locations in app/exercises/bcm_welcome_meals.r:
# Line 2 (comment)
# BCM - Welcome Meals Survey - Survey ID 5404
# Line 7 (id field)
id = "5404",
# Line 22 (survey_id parameter)
survey_id = 5404L,
# Line 26 (source_name)
source_name = "BCM - Welcome Meals (Survey 5404)"Step 4: Test
shiny::runApp("app")
# Select "BCM - Welcome Meals"
# ✓ Data loads successfully!Key Lessons
xlsFormId vs surveyID:
xlsFormIdis the form identifier (relatively stable)surveyIDis the data collection instance (can change when forms are republished)
Always verify survey IDs: Even if you think you know the ID, verify it via the API
Use the surveys list: The
get_household_surveys_list()method is invaluable for mapping xlsFormId to surveyIDHelper tool available: We created
tools/find_survey_by_xlsform.rfor this exact scenario
Quick Reference: Find Survey ID from xlsFormId
# Quick one-liner
library(reticulate); library(dplyr)
use_python(".venv/bin/python")
data_bridges_knots <- import("data_bridges_knots")
client <- data_bridges_knots$DataBridgesShapes("data_bridges_api_config.yaml")
surveys <- client$get_household_surveys_list(page = 1L)
surveys |> filter(xlsFormId == YOUR_XLSFORM_ID) |> select(surveyID, xlsFormId, xlsFormName)Or use the helper script:
source("tools/find_survey_by_xlsform.r")
find_survey_by_xlsform(YOUR_XLSFORM_ID)Next Steps for Using This Exercise
1. Survey ID (✓ Complete)
The correct survey ID (5404) has been identified and updated in the exercise file.
2. Verify Column Names
Once you have access to actual data, verify the column names match those used in:
Filtering:
Enumerator_Name(line 13)
Validations:
Beneficiary_ID(referenced in validation)Interviewee_Phone_Number(default in phone_uniqueness_rule)has_complaint(referenced in proportion check)
Visualizations:
meal_quality_goodmeal_quantity_sufficientmeal_temperature_appropriateserved_on_timestaff_courteoushas_complaintdistribution_locationmeal_typebeneficiary_genderbeneficiary_age_group
Update column names as needed to match your actual survey structure.
3. Adjust Validation Thresholds
Review and adjust validation thresholds based on program requirements:
# Duration (currently 5 min - 1 hour)
duration_check = duration_check_rule(
min_seconds = 300, # Adjust as needed
max_seconds = 3600 # Adjust as needed
)
# Complaint rate (currently max 10%)
complaint_rate_check = proportion_check_rule(
column_name = "has_complaint",
positive_value = "Yes",
max_proportion = 0.10, # Adjust as needed
description_text = "Complaint rate is not more than 10%"
)
# Missing data (currently max 30%)
missing_data_check = missing_data_rule(
max_missing_proportion = 0.30 # Adjust as needed
)4. Test the Exercise
Testing checklist:
5. Customize Visualizations
If needed, update the dashboard in app/R/visualization_helpers.r:
- Add/remove metric cards
- Adjust color thresholds
- Add new sections
- Reorder sections
- Change labels
Interactive Survey Discovery
New Tool: tools/discover_surveys.r
We’ve created an interactive discovery script that makes finding survey IDs easy!
Usage:
# Run from R console
source("tools/discover_surveys.r")
# Search for surveys
search_surveys("welcome meals")
# Inspect survey structure
inspect_survey(5400)
# Explore data interactively
View(current_survey_data)
names(current_survey_data)What it does:
- ✓ Connects to DataBridges API
- ✓ Lists available surveys (if supported)
- ✓ Searches surveys by keyword
- ✓ Inspects survey data structure
- ✓ Shows column names and types
- ✓ Identifies potential filter columns
- ✓ Displays sample data
- ✓ Loads data for interactive exploration
Example workflow:
1. source("tools/discover_surveys.r")
2. search_surveys("BCM")
3. inspect_survey(5400)
4. Review output: columns, filters, sample data
5. Note key information for exercise creation
6. Create exercise file with gathered info
This eliminates guesswork and makes it easy to understand survey structure before writing code!
Documentation for Future Developers
Comprehensive Guide: docs/ADDING_NEW_EXERCISES.md
This guide now includes a complete interactive discovery section that walks developers through:
- ✓ Survey ID discovery: Three methods with interactive script (recommended)
- ✓ Step-by-step discovery workflow: From running script to gathering all needed info
- ✓ Interactive exploration: Commands for exploring data structure
- ✓ Troubleshooting discovery: Common issues and solutions
- ✓ Complete process: Step-by-step exercise creation instructions
- ✓ Code examples: Working code for every step
- ✓ Reference: All available validation rules and UI components
- ✓ Troubleshooting: Common issues and solutions
- ✓ Best practices: Code quality, performance, testing
- ✓ Full example: Annotated complete exercise file
Quick Start Guide: QUICK_START_NEW_EXERCISE.md
A fast-track reference for experienced developers:
- ✓ Quick workflow: 3-step process (discover, create, test)
- ✓ Ready-to-use template: Copy-paste exercise template
- ✓ Common validation rules: Quick reference list
- ✓ Visualization components: Available UI components
- ✓ Common issues: Quick troubleshooting
- ✓ Key files: Where to find what
Time estimate: ~20 minutes from discovery to working exercise!
Future developers can follow these guides to add new exercises without needing to reverse-engineer the existing code.
Files Modified/Created
Created Files
- ✓
app/exercises/bcm_welcome_meals.r- Exercise definition - ✓
docs/ADDING_NEW_EXERCISES.md- Comprehensive developer guide (with interactive discovery section) - ✓
tools/discover_surveys.r- Interactive survey ID discovery script - ✓
QUICK_START_NEW_EXERCISE.md- Fast-track reference guide - ✓
BCM_WELCOME_MEALS_ADDITION.md- This summary document
Modified Files
- ✓
app/R/visualization_helpers.r- Addedbcm_welcome_meals_dashboard()function
Technical Notes
Exercise Structure Pattern
The exercise follows the established pattern:
exercise <- list(
# Metadata
id, name, type, description,
# UI Configuration
filter_column, filter_label,
# Functions
fetch_data = function(client) { ... },
prepare_data = function(raw_data) { ... },
visualizations = function(data) { ... },
# Validation Function (required)
run_validations = function(data) { ... }
)Validation Rule Usage
Uses reusable rules from app/R/validation_rules.r:
- Standard rules with default parameters
- Standard rules with custom parameters
- Exercise-specific rules for business logic
Visualization Components
Uses helper functions from app/R/visualization_helpers.r:
metric_card()- Percentage metricsbreakdown_card()- Categorical distributions- Responsive layout with
fluidRow()andcolumn()
Benefits of This Implementation
- No Manual Registration: Exercise auto-loads via registry
- Modular Design: Each exercise is self-contained
- Reusable Components: Validation rules and UI components shared
- Easy to Test: Exercise loads independently
- Maintainable: Clear structure, well-documented
- Extensible: New rules and components easy to add
- Documented: Comprehensive guide for future developers
Questions or Issues?
If you encounter issues:
- Check column names: Verify against actual survey data
- Review console logs: R console shows detailed error messages
- Test data fetch: Verify API connection and survey ID
- Consult examples: Look at
bcm_helpdesk_validation.randosm_helpdesk.r - Read documentation:
docs/ADDING_NEW_EXERCISES.mdhas troubleshooting section
Conclusion
The BCM - Welcome Meals exercise has been successfully added to the app with:
- ✓ Complete exercise definition
- ✓ Custom visualizations
- ✓ Comprehensive validation rules
- ✓ Automatic registration
- ✓ Developer documentation for future use
The exercise is ready to use once the actual survey ID and column names are verified and updated.