Longitudinal follow up, access to old data from froms


I am new to CHT

Ideally I would like to save some observation and classification done on the patient via app forms

I had a look at the card and I would like to save the “take home” (Observation and conditions) captured in a form by saving them in a card with a timestamp and the action_id

so later, in the form I could do something like below (${condition_name} to be replaced with actual field) to find when was the last time the patient had a condition:


but it is not clear to me if the card have history or if they are just computed therefore before trying to code everything I would like to have some feedback

Does it look like a realistic approach ?

Thanks in advance

1 Like

Hi @delcroip , welcome to the CHT forum and thank you for posting the question. Your approach of storing some historical records for patient and accessing them later in the form sounds right approach.

We’ve some example of setting these values on our standard config here and example of using this variable in forms in pregnancy home visit form here in context_vars group starting at row 45.

We hope this is helpful. Please let us know how it goes.

1 Like

Thank you for your reply

so if I understand well, you modify the context via modifyContext and then you access it,

that brings other noob questions

  • what is the scope of the context ? is it the patient ? is it the area/facility ?
  • can you have list or dict in the context ?

Based on your response I guess that the card fields cannot be used for that usecase, is that correct ?

Best regards

so if I understand well, you modify the context via modifyContext and then you access it,

Yes. Technically speaking, I believe the contact summary (along with its context) is re-calculated (using the log from contact-summary.templated.js) whenever it is accessed (e.g. when a form is opened for that contact) based on the current data for that contact and the associated reports.

  • what is the scope of the context ? is it the patient ? is it the area/facility ?

The context is only for the single contact (whoever the form is being filled out for).

  • can you have list or dict in the context ?

Unfortunately, I am not sure, and have not had a chance yet to test this.

One thing I will say is if you look at the logic for the risk_factor_codes from @yuv’s example that he linked, they are compiled from data pulled from various types of forms (and then serialized into a comma separated string). It may be that you just need to consider more how you would want to consume you summary data within the form such that you could load it from a serialized field (similar to how the risk_factor_codes data gets used in the pregnancy home visit form.

1 Like

thanks a lot,

but I cannot make it work, the report fields are empty and the model has always an empty <instance id="contact-summary"/> on the model

here my card:

    label: 'contact.profile.imm.child',
    appliesToType: 'report',
    appliesIf:   (report) => { return report && immunizationForms.includes(report.form);},
    fields:  getImmFileds(allReports),
    modifyContext: modifyImmContext,

and here the modifyImmContext function (from extras)

function modifyImmContext(ctx, allReports){
  var immunizations = initImmunizations(allReports);
  // just to have something in the context that cannot fails
  // add the entry in the context only if there is a value
  immunizations.entries.forEach(function(entry) {
    const [key, imm] = entry;
    if (isDate(imm)){
      ctx['imm_'+key]= imm;

I even added and display a calculate for instance('contact-summary')/context/form_type but it remains empty (I I don’t have to check initImmunizations(allReports))

Any Idea why it fails ? Also would it be possible to add the logger in the contact-summary.templated.js

Hmm, I think there is something not quite right with how this card is getting triggered for you in the first place. I tested this out locally and did not have any problems getting the form_type field to be set in the contact summary and then get picked up in the forms.

Just to be sure, you do not actually see the card displayed for the contact when looking at the contact’s page in the “Person” tab, right? The only explanation I can think of for the form_type value to not be set for you is that the contact summary card is actually not getting triggered at all for the contact.

Some things to consider:

  • To be included in the contact-summary calculation for a particular contact, the reports need to be related to the contact (via patient_id/patient_uuid/etc)
  • To load contact-summary data in a form, the form must be opened from the contact’s page on the “People” tab (so that the contact is in context). Contact summary data is not loaded for forms opened from the “Reports” tab.

I am not sure about more complicated logging strategies, but I do know that if you just add console.log statements to contact-summary.templated.js, they will get printed to the browser console when the contact summary is calculated. (Remember the contact-summary should get re-calculated any time the contact’s page is loaded.)

1 Like


I am still not able to get anything but I am starting to think it is related to my setup somehow

I see no card on the contact (people)

I removed all containers, restarted them (with the update compose) but it did not change anything

I noticed that GET to _changes are never successful (no html answer : no 200, 404,…) ex :


server (no prod data)
https://cht-tchad.swisstph-mis.ch forum / Forum@2023


As far as I can tell, things on your test instance seem to be functioning normally (docs are written as expected, Sentinel is processing changes normally, etc).

Additionally, I was able to successfully get the Active Pregnancy summary card to show up for a patient (and was able to see contact-summary data loaded in a form). Check out this test patient with an active pregnancy. If you open a new “Pregnancy home visit” form for that patient, you should see the “Expected Date of Delivery” populated from the contact-summary data. This seems to indicate that there is no systemic issue with calculating/displaying contact summary data on this instance.

However, when I looked at the settings doc for the instance (the one that is populated with your config when you run the cht-conf command cht upload-app-settings) I could clearly see the card config for ‘contact.profile.pregnancy.active’ (including its modifyContext function), but I did not see anything related ‘contact.profile.imm.child’ (or any other cards with a modifyContext function at all). This makes me wonder if your intended contact-summary config is getting deployed at all?

How are you deploying the config changes? (Remember, for changes to the contact-summary.templated.js to be included, you need to run the compile-app-settings action before running upload-app-settings.)

I did upload it in the beginning, then with the changes I was doing in the js files I was mainly building the settings (to ensure that the card had no error) … but not uploading it: noob error I guess

It crashes now but that is a good news I should be able to trouble shoot next week

A big thank you for your support


1 Like

Sounds good! Let me know how it goes!


it finally works :champagne: thank you for the support

Troubleshooting is a bit tough because of the minifying, it would be great if we could prevent it or at least having the map; that being said the ‘console.log()’ saved me

I was a bit unsettled by the scope of the different variables and the default function params

in my code below I call ‘allReports’ within a function where it is neither a param nor defined in the function

// show a summary for the patient
cards.push(  {
    label: 'contact.profile.imm.child',
    appliesTo: 'contacts',
    appliesToType: 'person',
    appliesIf:  function(){ return getAgeInMonths()<18;},
    fields:  getImmFileds(allReports),
    modifyContext: function (ctx){
      var immunizations = initImmunizations(allReports);
      // add the entry in the context only if there is a value
      Object.entries(immunizations).forEach(([key, value]) => {
          if (value !== null ){

Also it seems that the default positional param are something like

    appliesIf:  callback(contact, report)
    fields: array (mandatory, at least []),
    modifyContext: callback (ctx, report )

which is not 100 % aligned with the docs so I wonder if there other type of param possible

Next challenge will be to try if a context can hold an object in order to use the object params as filter for the context-summary/context instance like

instance("context-summary")/context[type='condition' && name='blabla']/timestamp

but for that I would need to find out how the app is retrieving the context from the BE (could not find it on the network debug tab)

That is great to hear @delcroip! I am very curious to hear how it goes with the object/filter. I feel like everything should just work here (based on my reading of the impl code), but it is hard to tell for sure until we try it…

The tricky part about the contact-summary is that it is not actually retrieved from the server (or really stored as a data-record at all). It is calculated on-the-fly in the client code by calling the minified code stored on the settings doc. The actual code stored in the settings doc is compiled from your contact-summary.templated.js config by logic in the cht-conf util. So, cht-conf takes your config and packages it into a single “generator function” that will emit the contact-summary based on your specific config. The code for that function is stored on the settings doc. Then, the client code will “get” the contact-summary by calling that function.

in my code below I call ‘allReports’ within a function where it is neither a param nor defined in the function

I am guessing that your contact-summary.templated.js has some code like the default config that is setting allReports as a global value.

which is not 100 % aligned with the docs so I wonder if there other type of param possible

So, I double-checked the cht-conf code (from the link I posted above) and I agree that the docs appear to be lacking in the details of a few of the params here. I have opened this PR to update the docs. Let me know if this looks correct or if you think anything else can be improved here!