{
  "$schema": "https://schemas.rasd.dev/form/v1.json",
  "rasd": "1.0",
  "id": "wash-water-point-2026",
  "version": "1.2",
  "meta": {
    "title": { "en": "Water point functionality survey", "fr": "Enquête de fonctionnalité des points d'eau" },
    "description": {
      "en": "Quarterly inspection of a community water point: physical state, yield, water quality and management committee.",
      "fr": "Inspection trimestrielle d'un point d'eau communautaire : état physique, débit, qualité de l'eau et comité de gestion."
    },
    "tags": ["wash", "infrastructure", "quarterly"],
    "author": "WASH cluster",
    "createdAt": "2026-02-10T09:00:00Z",
    "changelog": {
      "en": "Added the catchment perimeter and the committee interview recording.",
      "fr": "Ajout du périmètre de captage et de l'enregistrement de l'entretien avec le comité."
    }
  },
  "settings": {
    "defaultLocale": "en",
    "locales": ["en", "fr"],
    "navigation": "scroll",
    "showProgress": true,
    "allowDrafts": true,
    "autosaveMs": 3000,
    "instanceName": "concat(${village}, ' — ', ${point_code})",
    "submissionIdPrefix": "WP",
    "numbering": "latn",
    "audit": { "enabled": true, "trackChanges": true },
    "theme": { "themeId": "rasd-field" }
  },
  "choiceLists": {
    "point_type": {
      "choices": [
        { "value": "borehole_handpump", "label": { "en": "Borehole with handpump", "fr": "Forage avec pompe à main" } },
        { "value": "borehole_solar", "label": { "en": "Borehole with solar pump", "fr": "Forage avec pompe solaire" } },
        { "value": "protected_spring", "label": { "en": "Protected spring", "fr": "Source protégée" } },
        { "value": "dug_well", "label": { "en": "Hand-dug well", "fr": "Puits creusé à la main" } },
        { "value": "piped_tapstand", "label": { "en": "Piped tapstand", "fr": "Borne-fontaine" } }
      ]
    },
    "functionality": {
      "choices": [
        { "value": "functional", "label": { "en": "Functional", "fr": "Fonctionnel" } },
        { "value": "partial", "label": { "en": "Functional but needs repair", "fr": "Fonctionnel mais nécessite une réparation" } },
        { "value": "non_functional", "label": { "en": "Not functional", "fr": "Non fonctionnel" } },
        { "value": "abandoned", "label": { "en": "Abandoned", "fr": "Abandonné" } }
      ]
    },
    "fault": {
      "choices": [
        { "value": "pump_worn", "label": { "en": "Worn pump parts", "fr": "Pièces de pompe usées" } },
        { "value": "rising_main", "label": { "en": "Broken rising main", "fr": "Colonne montante cassée" } },
        { "value": "apron_cracked", "label": { "en": "Cracked apron or drainage", "fr": "Margelle ou drainage fissuré" } },
        { "value": "dry", "label": { "en": "Source dry or low yield", "fr": "Source tarie ou faible débit" } },
        { "value": "vandalism", "label": { "en": "Vandalism or theft", "fr": "Vandalisme ou vol" } },
        { "value": "no_fuel_power", "label": { "en": "No fuel or power", "fr": "Pas de carburant ni d'électricité" } }
      ]
    },
    "committee_state": {
      "choices": [
        { "value": "active", "label": { "en": "Active, meets regularly", "fr": "Actif, se réunit régulièrement" } },
        { "value": "dormant", "label": { "en": "Exists but dormant", "fr": "Existe mais inactif" } },
        { "value": "none", "label": { "en": "No committee", "fr": "Aucun comité" } }
      ]
    },
    "villages": {
      "source": { "type": "dataset", "dataset": "villages" },
      "valueKey": "code",
      "labelKey": "name"
    }
  },
  "datasets": [
    {
      "name": "villages",
      "source": "inline",
      "keyField": "code",
      "columns": [
        { "name": "code", "type": "string", "label": { "en": "Code", "fr": "Code" } },
        { "name": "name", "type": "string", "label": { "en": "Village", "fr": "Village" } },
        { "name": "commune", "type": "string", "label": { "en": "Commune", "fr": "Commune" } },
        { "name": "population", "type": "number", "label": { "en": "Population", "fr": "Population" } }
      ],
      "inline": [
        { "code": "TL-042", "name": "Tillabéri Nord", "commune": "Tillabéri", "population": 4120 },
        { "code": "TL-043", "name": "Sakoira", "commune": "Tillabéri", "population": 2890 },
        { "code": "TH-011", "name": "Téra Centre", "commune": "Téra", "population": 7350 },
        { "code": "TH-012", "name": "Diagourou", "commune": "Téra", "population": 1980 }
      ]
    }
  ],
  "pages": [
    {
      "id": "identification",
      "title": { "en": "Identification", "fr": "Identification" },
      "elements": [
        {
          "type": "note",
          "name": "intro",
          "label": {
            "en": "Complete one form per water point. If the point cannot be reached, record why on the last page rather than abandoning the form.",
            "fr": "Remplissez un formulaire par point d'eau. Si le point est inaccessible, indiquez-en la raison à la dernière page plutôt que d'abandonner le formulaire."
          }
        },
        {
          "type": "select_one",
          "name": "village",
          "label": { "en": "Village", "fr": "Village" },
          "required": true,
          "props": { "list": "villages" }
        },
        {
          "type": "text",
          "name": "point_code",
          "label": { "en": "Water point code", "fr": "Code du point d'eau" },
          "hint": { "en": "Stencilled on the apron, e.g. WP-1183", "fr": "Peint sur la margelle, p. ex. WP-1183" },
          "required": true,
          "validators": [
            {
              "type": "regex",
              "pattern": "^WP-\\d{3,5}$",
              "message": { "en": "Codes look like WP-1183", "fr": "Les codes ressemblent à WP-1183" }
            }
          ]
        },
        {
          "type": "datetime",
          "name": "visit_at",
          "label": { "en": "Date and time of visit", "fr": "Date et heure de la visite" },
          "required": true,
          "default": { "expr": "now()" },
          "props": { "max": "2027-12-31T23:59:59Z" }
        },
        {
          "type": "geopoint",
          "name": "point_location",
          "label": { "en": "Water point location", "fr": "Emplacement du point d'eau" },
          "required": true,
          "props": { "accuracyThreshold": 15, "autoCapture": true, "map": true }
        }
      ]
    },
    {
      "id": "infrastructure",
      "title": { "en": "Infrastructure", "fr": "Infrastructure" },
      "elements": [
        {
          "type": "select_one",
          "name": "point_type",
          "label": { "en": "Type of water point", "fr": "Type de point d'eau" },
          "required": true,
          "props": { "list": "point_type" }
        },
        {
          "type": "number",
          "name": "year_built",
          "label": { "en": "Year constructed", "fr": "Année de construction" },
          "props": { "min": 1950, "max": 2026, "step": 1 },
          "validators": [
            {
              "type": "range",
              "min": 1950,
              "max": 2026,
              "message": { "en": "Between 1950 and this year", "fr": "Entre 1950 et cette année" }
            }
          ]
        },
        {
          "type": "geoshape",
          "name": "catchment",
          "label": { "en": "Catchment protection perimeter", "fr": "Périmètre de protection du captage" },
          "hint": {
            "en": "Walk the fenced perimeter. Three points minimum; the shape closes itself.",
            "fr": "Parcourez le périmètre clôturé. Trois points minimum ; la forme se referme d'elle-même."
          },
          "relevant": "${point_type} != 'piped_tapstand'",
          "props": { "mode": "manual", "minPoints": 3, "allowManual": true, "map": true, "accuracyThreshold": 20 }
        },
        {
          "type": "select_one",
          "name": "functionality",
          "label": { "en": "Functionality on the day of visit", "fr": "Fonctionnalité le jour de la visite" },
          "required": true,
          "props": { "list": "functionality" }
        },
        {
          "type": "select_multiple",
          "name": "faults",
          "label": { "en": "Faults observed", "fr": "Défauts observés" },
          "relevant": "${functionality} = 'partial' or ${functionality} = 'non_functional'",
          "required": "${functionality} = 'non_functional'",
          "props": { "list": "fault" }
        },
        {
          "type": "number",
          "name": "days_down",
          "label": { "en": "Days out of service", "fr": "Jours hors service" },
          "relevant": "${functionality} = 'non_functional' or ${functionality} = 'abandoned'",
          "props": { "min": 0, "max": 3650, "step": 1 }
        },
        {
          "type": "calculate",
          "name": "downtime_band",
          "calculate": "if(${days_down} > 90, 'chronic', if(${days_down} > 14, 'extended', 'recent'))"
        }
      ]
    },
    {
      "id": "yield_quality",
      "title": { "en": "Yield and water quality", "fr": "Débit et qualité de l'eau" },
      "elements": [
        {
          "type": "note",
          "name": "yield_note",
          "label": {
            "en": "Run the pump for two minutes before taking any reading.",
            "fr": "Faites fonctionner la pompe pendant deux minutes avant toute mesure."
          },
          "relevant": "${functionality} = 'functional' or ${functionality} = 'partial'"
        },
        {
          "type": "number",
          "name": "yield_lpm",
          "label": { "en": "Yield (litres per minute)", "fr": "Débit (litres par minute)" },
          "relevant": "${functionality} = 'functional' or ${functionality} = 'partial'",
          "required": true,
          "props": { "min": 0, "max": 500, "step": 0.1 }
        },
        {
          "type": "calculate",
          "name": "people_per_point",
          "calculate": "if(${yield_lpm} > 0, round(${yield_lpm} * 60 * 8 / 20), 0)"
        },
        {
          "type": "note",
          "name": "capacity_note",
          "label": {
            "en": "At this yield the point can serve roughly {people_per_point} people a day at 20 litres each.",
            "fr": "À ce débit, le point peut desservir environ {people_per_point} personnes par jour à raison de 20 litres chacune."
          },
          "relevant": "${yield_lpm} > 0"
        },
        {
          "type": "number",
          "name": "ph",
          "label": { "en": "pH", "fr": "pH" },
          "relevant": "${functionality} = 'functional' or ${functionality} = 'partial'",
          "props": { "min": 0, "max": 14, "step": 0.1 },
          "validators": [
            {
              "type": "range",
              "min": 6.5,
              "max": 8.5,
              "severity": "warning",
              "message": {
                "en": "Outside the 6.5–8.5 guideline; re-test before reporting",
                "fr": "Hors de la plage recommandée 6,5–8,5 ; refaites le test avant de signaler"
              }
            }
          ]
        },
        {
          "type": "number",
          "name": "ec_uscm",
          "label": { "en": "Electrical conductivity (µS/cm)", "fr": "Conductivité électrique (µS/cm)" },
          "relevant": "${functionality} = 'functional' or ${functionality} = 'partial'",
          "props": { "min": 0, "max": 20000, "step": 1 },
          "validators": [
            {
              "type": "custom",
              "id": "wash.ec-plausible",
              "message": {
                "en": "Conductivity is far from the district norm; confirm the meter is calibrated",
                "fr": "La conductivité s'écarte fortement de la norme du district ; vérifiez l'étalonnage du conductimètre"
              },
              "severity": "warning"
            }
          ]
        },
        {
          "type": "image",
          "name": "apron_photo",
          "label": { "en": "Photograph of the apron and drainage", "fr": "Photo de la margelle et du drainage" },
          "required": true,
          "props": { "source": "camera", "maxPixels": 1600, "quality": 0.7, "geotag": true }
        }
      ]
    },
    {
      "id": "management",
      "title": { "en": "Management committee", "fr": "Comité de gestion" },
      "elements": [
        {
          "type": "select_one",
          "name": "committee",
          "label": { "en": "Water point committee", "fr": "Comité du point d'eau" },
          "required": true,
          "props": { "list": "committee_state" }
        },
        {
          "type": "number",
          "name": "committee_women",
          "label": { "en": "Women on the committee", "fr": "Femmes au sein du comité" },
          "relevant": "${committee} != 'none'",
          "props": { "min": 0, "max": 40, "step": 1 }
        },
        {
          "type": "number",
          "name": "committee_total",
          "label": { "en": "Committee members in total", "fr": "Membres du comité au total" },
          "relevant": "${committee} != 'none'",
          "props": { "min": 0, "max": 40, "step": 1 },
          "constraint": ". >= ${committee_women}",
          "constraintMessage": {
            "en": "The total cannot be smaller than the number of women",
            "fr": "Le total ne peut pas être inférieur au nombre de femmes"
          }
        },
        {
          "type": "audio",
          "name": "committee_interview",
          "label": { "en": "Recording of the committee interview", "fr": "Enregistrement de l'entretien avec le comité" },
          "hint": {
            "en": "Optional, and only with spoken permission. Five minutes is plenty.",
            "fr": "Facultatif, et uniquement avec autorisation orale. Cinq minutes suffisent amplement."
          },
          "relevant": "${committee} = 'active' or ${committee} = 'dormant'",
          "props": { "maxDurationSeconds": 600, "maxBytes": 8000000 }
        },
        {
          "type": "number",
          "name": "tariff_local",
          "label": { "en": "Monthly household tariff (local currency)", "fr": "Tarif mensuel par ménage (monnaie locale)" },
          "relevant": "${committee} = 'active'",
          "props": { "min": 0, "max": 100000, "step": 25 }
        }
      ]
    },
    {
      "id": "closing",
      "title": { "en": "Closing", "fr": "Clôture" },
      "elements": [
        {
          "type": "text",
          "name": "recommendation",
          "label": { "en": "Recommended action", "fr": "Action recommandée" },
          "props": { "multiline": true, "maxLength": 600 },
          "required": "${functionality} != 'functional'"
        },
        {
          "type": "text",
          "name": "not_reached_reason",
          "label": { "en": "If the point could not be inspected, why?", "fr": "Si le point n'a pas pu être inspecté, pourquoi ?" },
          "props": { "multiline": true, "maxLength": 400 }
        },
        {
          "type": "signature",
          "name": "enumerator_signature",
          "label": { "en": "Enumerator signature", "fr": "Signature de l'enquêteur" },
          "required": true
        },
        {
          "type": "hidden",
          "name": "survey_round",
          "default": { "value": "2026-Q1" }
        }
      ]
    }
  ],
  "logic": {
    "triggers": [
      {
        "id": "clear-faults-when-functional",
        "when": "${functionality} = 'functional'",
        "actions": [
          { "type": "clearValue", "target": "faults" },
          { "type": "clearValue", "target": "days_down" }
        ]
      },
      {
        "id": "warn-on-chronic-downtime",
        "when": "${downtime_band} = 'chronic'",
        "actions": [
          {
            "type": "showMessage",
            "message": {
              "en": "This point has been down for more than three months. Flag it to the cluster today, not at the end of the round.",
              "fr": "Ce point est hors service depuis plus de trois mois. Signalez-le au cluster aujourd'hui, pas à la fin du cycle."
            }
          }
        ]
      }
    ]
  }
}
