Temporarily increase future task 180 limit

As the title states, is there a way to lengthen the 180 cut off for future task generation?

The app was launched last year around 1 Nov, and unfortunately at the time we did not have the capacity to implement tasks. Now, many people have been registered & received their initial assessments, which will be ignored for follow up visits tasks. As a result we’re concerned some might not receive the care they need.

It would be very helpful to temporarily increase the future task limit in order to make sure that all patients will be tended to.

Another question around the 180 day cutoff limit is if the end value is included in the limit. For example, if a task is right on the cusp of the 180 day limit, and has an end value of 60, will the task stick around for an additional 2 months?

Hi @Anro

I don’t believe there is a way to increase the limit.
What sort of tasks do you think will not be generated? Can you maybe change your task code so that you generate those tasks with a timely date instead of an old date?

Hi @diana,

It is a task that is triggered roughly 4 months after a (COPC) report has been captured for an individual. The requirement is make sure our patient details are still correct, so we use an intermediary form to link to a contact-edit form. Bit of a weird one. We call this a “follow-up” task.

Because our patient COPC reports, in most cases, have been captured more than 7 months ago, and the limit of future task scheduling is 180 days, we fear we’ll be unable to get our tasks to trigger correctly.

If we’re able to “extend” the expiry by adding 2 months at the end of the 180 day scheduled task (after COPC report completion), it should pick up nicely.
My understanding of the task is a little sparse still, would appreciate your thoughts.

The task code for reference:

{
    icon: 'icon-follow-up',
    name: 'follow-up-task',
    title: 'task.follow-up',
    appliesTo: 'reports',
    appliesToType: ['copc'],
    actions: [{ form: 'intermediary' }],
    events: [
      {
        id: 'initial-follow-up',
        days: 180,
        start: 0,
        end: 30,
      },
    ],
    resolvedIf: function (contact, _, event, dueDate) {
      const isCHW = user.contact_type === 'chw';
      const follow_up_date = contact.contact.follow_up_date;
      const converted_follow_up_date = luxon.DateTime.fromISO(follow_up_date);
      const isFeatureFlaggedHouseholdMember = contact.contact.contact_type = 'ff_hhm';
      const isDeceased = contact.contact.death_flag === 'yes' && !contact.contact.date_of_death;
      // eslint-disable-next-line eqeqeq
      const migratedToNonTrackedParent = contact.contact.out_migrated_to != null && contact.contact.out_migrated_to !== '' && contact.contact.out_migrated_to !== 'same_indawo';
      
      const isDone = converted_follow_up_date >= Utils.addDate(dueDate, -event.start).getTime() && converted_follow_up_date <= Utils.addDate(dueDate, event.end + 1).getTime();

      return !isCHW || !isFeatureFlaggedHouseholdMember || isDeceased || migratedToNonTrackedParent || isDone;
    }

Hi @Anro

You could use events[n].dueDate, which is a function that takes the event, contact and report as parameters, and have this function return a timely date for contacts or reports that you know were submitted before you added tasks.

Documentation about dueDate here: tasks.js | Community Health Toolkit

Hi @diana,

I’m guessing that could be something as simple as:

{
    icon: 'icon-follow-up',
    name: 'follow-up-task',
    title: 'task.follow-up',
    appliesTo: 'reports',
    appliesToType: ['copc'],
    actions: [{ form: 'intermediary' }],
    events: [
      {
        id: 'initial-follow-up',
        start: 0,
        end: 30,
        dueDate: (e, c, r) => {
          const { reported_date } = r;
          const convertedReportedDate = luxon.DateTime.fromMillis(reported_date);

          if(convertedReportedDate.diff(luxon.DateTime.now()).days > 180){
            return luxon.DateTime.now().plus({days: 15}).toJSDate();
          }
          else {
            return luxonDueDate.fromStart('year').plus({years: 1, months: 1}).toJSDate();
          }
        },
      },
    ],
...

In addition to this we have also worked on creating a repeating task as noted in the example documentation. Unfortunately, the code for the getNextFamilySurveyDate was not listed in the documentation so we dreamt up our own:

const luxon = require('luxon');

const getLuxonYearInEqualIntervals = (offsetYearStart = 1, offsetYearEnd = 1, sections = 3, now = luxon.DateTime.now()) => {
  const start_of_year = now.startOf('year').plus({months: offsetYearStart});
  const end_of_year = now.endOf('year').plus({months: -offsetYearEnd});
  return luxon.Interval.fromDateTimes(start_of_year, end_of_year).divideEqually(sections);
};

const getFollowUpDueDate = (event, contact, report, today) => {
  const luxonToday = today || luxon.DateTime.now();
  const { reported_date } = report; // eg. reported_date : 1722607073828
  const convertedReportedDate = luxon.DateTime.fromMillis(reported_date);
  // Offset the start and end by 1 month since not many people will work during January and December.
  // Split the year into 3 equal sections
  const parts = getLuxonYearInEqualIntervals(1, 1, 3);
  let luxonDueDate = parts[0].end.plus({ years: 1 }); 
  for (const part of parts) {
    const intendedDueDate = part.end.plus({days: -event.end});
    const intendedDueDateRange = luxon.Interval.fromDateTimes(
      intendedDueDate.plus({days: -event.start}),
      intendedDueDate.plus({days: event.end})
    );
    if(luxonToday < intendedDueDate && !intendedDueDateRange.contains(convertedReportedDate)){
      luxonDueDate = part.end;
      break; // Exit early after finding a valid due date
      // An alternative to this would be to reverse the array
      // That way an earlier valid date will overwrite the previous selected value
    }
  }

  return luxonDueDate.plus({days: -event.end}).toJSDate();
};

module.exports = {
  getLuxonYearInEqualIntervals,
  getFollowUpDueDate
};

Have we perhaps neglected to consider something when hooking the above getFollowUpDueDate method into the task’s dueDate function?

This is how development works, you dream up your own code that suits your needs :slight_smile:
Listed where, are you copying config from somewhere?

In your due-date function, I would change the condition:

 if(convertedReportedDate.diff(luxon.DateTime.now()).days > 180){

to be less generic. You’ve said that these tasks weren’t created for some users because you hadn’t set up tasks yet. If you go this generic 180 days ago way, you will end up creating these tasks for new contacts that have been part of the follow-up workflow.
If you know at what time you introduced the follow-up workflow, I would just use that date in the condition instead:

if (reported_date < <the date when we introduced the workflow>) {

Hi @diana,

That is true, while it won’t fit everyone’s needs, we were just hoping to add a “periodic task” impl snippet to the CHT docs to serve as an example :slight_smile:. Would that be beneficial?

The task code we mostly wrote on our own, after confirming luxon availability in this thread. We did, however, take inspiration from this CHT doumentation, specifically // Option 2: Place-based task: Family survey every 6 months to create a task that fires every few months.

Oof that’s quite a slip on my part, thank you for catching that! We’ll implement it accordingly :slight_smile:.

1 Like