Restrict can_delete_contact permission to specific hierarchy for role

We require CHWs to have the ability to delete hierarchy items (plots/households/household members) when they are no longer relevant or have been created in error.

Unfortunately, we have encountered a scenario where a CHW accidentally deleted themselves. There is also a real possibility of accidentally deleting other CHWs and impacting their work.

Is there a way to conditionally show the ‘delete’ option to the user only when they are on certain levels of the hierarchy?

1 Like

As far as I can tell from the code there is no way to control what contacts a user can delete. Clearly this is not ideal (and it is a serious challenge that user contacts could be accidentally deleted). I have logged a new issue for this:

Please feel free to add any additional context or details to the issue! One clarification that would be helpful here is if you could include some info abut the workflow(s) where the CHW users need to have the can_delete_contact permission. Thanks!

2 Likes

Just to be clear, are you talking about online or offline users? The reason I ask is that offline users normally can’t see (and therefore can’t delete) other CHWs at the same level in the hierarchy (ie “peers”). Perhaps you could describe your hierarchy as well. Thanks!

1 Like

The CHWs are offline users.
That’s interesting, as in our runs the CHWs have always been able to see their peers.
See below screenshots where I logged in as a test CHW:


Do you believe this could be a fundamental fault in our hierarchy?
Our hierarchy structure is as follows

  1. NPO
    a) District health office (DHO)
  2. Team Area
    a) Team Lead
  3. Indawo
    a) CHW
  4. Plot/Location
  5. Household
    a) Household member

Our hierarchy in code:

"place_hierarchy_types": [
    "npo",
    "team_area",
    "indawo",
    "dwelling",
    "household"
  ],
  "contact_types": [
    {
      "id": "npo",
      "name_key": "contact.type.npo",
      "group_key": "contact.type.npo.plural",
      "create_key": "contact.type.npo.new",
      "edit_key": "contact.type.place.edit",
      "icon": "wcg-npo",
      "create_form": "form:contact:npo:create",
      "edit_form": "form:contact:npo:edit"
    },
    {
      "id": "dho",
      "name_key": "contact.type.dho",
      "group_key": "contact.type.dho.plural",
      "create_key": "contact.type.dho.new",
      "edit_key": "contact.type.dho.edit",
      "primary_contact_key": "contact.type.dho-primary",
      "parents": [
        "npo"
      ],
      "icon": "wcg-dho",
      "create_form": "form:contact:dho:create",
      "edit_form": "form:contact:dho:edit",
      "person": true
    },
    {
      "id": "team_area",
      "name_key": "contact.type.team_area",
      "group_key": "contact.type.team_area.plural",
      "create_key": "contact.type.team_area.new",
      "edit_key": "contact.type.place.edit",
      "parents": [
        "npo"
      ],
      "icon": "wcg-team_area",
      "create_form": "form:contact:team_area:create",
      "edit_form": "form:contact:team_area:edit"
    },
    {
      "id": "team_lead",
      "name_key": "contact.type.team_lead",
      "group_key": "contact.type.team_lead.plural",
      "create_key": "contact.type.team_lead.new",
      "edit_key": "contact.type.team_lead.edit",
      "primary_contact_key": "contact.type.team_lead-primary",
      "parents": [
        "team_area"
      ],
      "icon": "wcg-team_lead",
      "create_form": "form:contact:team_lead:create",
      "edit_form": "form:contact:team_lead:edit",
      "person": true
    },
    {
      "id": "indawo",
      "name_key": "contact.type.indawo",
      "group_key": "contact.type.indawo.plural",
      "create_key": "contact.type.indawo.new",
      "edit_key": "contact.type.place.edit",
      "parents": [
        "team_area"
      ],
      "icon": "wcg-indawo",
      "create_form": "form:contact:indawo:create",
      "edit_form": "form:contact:indawo:edit"
    },
    {
      "id": "chw",
      "name_key": "contact.type.chw",
      "group_key": "contact.type.chw.plural",
      "create_key": "contact.type.chw.new",
      "edit_key": "contact.type.chw.edit",
      "primary_contact_key": "contact.type.chw-primary",
      "parents": [
        "indawo"
      ],
      "icon": "wcg-chw",
      "create_form": "form:contact:chw:create",
      "edit_form": "form:contact:chw:edit",
      "person": true
    },
    {
      "id": "dwelling",
      "name_key": "contact.type.dwelling",
      "group_key": "contact.type.dwelling.plural",
      "create_key": "contact.type.dwelling.new",
      "edit_key": "contact.type.place.edit",
      "parents": [
        "indawo"
      ],
      "icon": "wcg-dwelling",
      "create_form": "form:contact:dwelling:create",
      "edit_form": "form:contact:dwelling:edit"
    },
    {
      "id": "household",
      "name_key": "contact.type.household",
      "group_key": "contact.type.household.plural",
      "create_key": "contact.type.household.new",
      "edit_key": "contact.type.household.edit",
      "parents": [
        "dwelling"
      ],
      "icon": "wcg-household",
      "create_form": "form:contact:household:create",
      "edit_form": "form:contact:household:edit",
      "count_visits": true
    },
    {
      "id": "hhm",
      "name_key": "contact.type.hhm",
      "group_key": "contact.type.hhm.plural",
      "create_key": "contact.type.hhm.new",
      "edit_key": "contact.type.hhm.edit",
      "primary_contact_key": "contact.type.hhm-primary",
      "parents": [
        "household"
      ],
      "icon": "wcg-hhm",
      "create_form": "form:contact:hhm:create",
      "edit_form": "form:contact:hhm:edit",
      "person": true
    }
  ]

The CHWs would need deletion permissions on the Location/Plot, Household, and Household member levels.

Location/Plot:

  1. An area of land that could contain multiple households.
  2. Captures GPS points.
  3. Nature of the entity (residential, health service, retail)

Household:

  1. Verbal consent
  2. visit date
  3. household head
  4. Two reports can be completed for this entity.

Household member:

  1. usual personal information
  2. clinic number
  3. Two reports can be completed for this entity.

This would help the CHWs delete entries that were captured in error, or delete entries that are no longer of relevance - adhering to training.
We’ve recently become aware of quite a few duplicate captures, which we believe was caused in part by the 50 contact limit bug we had on our CHT instance (since been resolved).
We’re updating our training to include the usage of the search bar, to also help mitigate such captures.
That being said, giving the CHW the ability to correct incorrect captures early on would help significantly.

Since you have multiple CHWs associated to the same Indawo, it makes sense as to why they would see their peers now, because they have the same parent. I think it’s more common to see each CHW have their own parent. It’s totally fine the way you have it but of course there’s a bigger risk of multiple CHWs editing the same document.

The thing that does look a bit odd to me though is the order in which things are showing up on the left side list on the Contacts tab.

In your hierarchy, indawo is above (ie parent of) dwelling, but in the screenshot “Indawo” is below your “Plots” (dwelling). That doesn’t look right to me… what do you think @jkuester @Jennifer_Quesada? I just tested on 4.5 and even when you “Sort” alphabetically, the user’s place is always at the top of the list.

1 Like

Thank you for clarifying. Our usual approach is 1 CHW per parent (Indawo), however there are cases where a CHW is accompanied by another CHW due to safety reasons or simply because they operate in pairs.
I do appreciate highlighting the risks of editing the same record. For interest sake, how would the system handle such an occurrence?

That’s a good point, I have not noticed it before.
Investigated this by logging in as a dho, team lead & CHW. On first glance it seemed as if only the dho’s place ordering is being respected. However, when toggling between “Alphabetically” and “By date last visited” in the search bar, seems to switch some of the place positions.
We’re currently on CHT version 4.2.2, has this “fixed parent” item been only implemented on later versions?

Default views:



Switch ordering:



Version:
image

Regarding the order of contacts in the sidebar, I did not see anything in the release notes since 4.2.2. that would indicate a change to the ordering, but I will defer this question to @Jennifer_Quesada who def has more visibility on any changes that have recently happened in this part of the code (and the expected behavior in general).

The short answer here is that if a document is located on two separate offline devices and both edit the same document while offline and then try to sync the changes to the server, the server will mark the doc as having a conflict and then pick one of the versions to use going forwards. Crucially, the version of the doc that the server picks as the “correct” one will get synced back to both client devices. So, after a successful sync, the same version of the document will be reflected on both client devices and on the server (and the changes from one of the users will not be seen anywhere). This approach emphasizes data consistency at the cost of “hiding” conflicting changes from the view of normal users.

It is possible to view all the docs that have conflicts (to determine if manual intervention is needed to recover hidden data). In Fauxton you can use the conflicts view in the medic-conflicts design to list all the docs with conflicts (open this Fauxton path for your CHT instance: _utils/#/database/medic/_design/medic-conflicts/_view/conflicts).

1 Like

Also, regarding how Couch “picks” a version to use, the Couch docs say:

CouchDB picks one arbitrary revision as the “winner”, using a deterministic algorithm so that the same choice will be made on all peers.

I do not know the details of the algorithm, but from experience with docs that have a conflict on just a single field (e.g. the contact’s name field was updated on both devices), the chosen “winner” was the version that was synced to the server first.

1 Like

I have seen the hierarchy order issue on another project. Oddly enough, it only happens to the CHWs; The order is fine for all other user types.

2 Likes

Hi @Femi do you have more information about the project where you saw the heirarchy order issue ?

Perhaps the CHT version they are on?
Their heirarchy ?

I am trying to reproduce it and figure out whether there is a bug somewhere in the code or whether it is a configuration issue.

Thank you.

Hi @michael @Femi @Ben_Kiarie ,

Has any new info regarding the list item ordering popped up?

Hi @Anro … We are not currently investigating it, but I have a haunch of what is going on here.

I think most of the UHC functionality in the list view / sorting is expecting a hierarchy whereby the contact_type you are counting visits for has the same parent as the CHW’s associated contact (left diagram, below).

In your hierarchy, your CHW is associated to the Indawo contact_type, you are counting visits for Households, but you have Plots (dwelling) as a hierarchy level in between (right diagram, below), so you’ll never see the visit dates in the list view and it doesn’t surprise me that sorting is behaving unpredictably.

Since “Last Visited” sorting won’t work in your hierarchy anyway, can you try removing the can_view_last_visited_date permission from your CHW users and see if that corrects the sorting (so the Indawo is always at the top, and the plots are sorted alphabetically). Another experiment would be to create a user associated to the Plot, and see if it works as expected (and sorting should work for this user type if they have the permission).

1 Like

One other thing to try would be to configure "count_visits":true for Plots (dwellings). What affect does that have for you?

Hi @michael, thank you for still following up on this even though it’s not a priority :pray:.

Your explanation makes solid sense. Thank you for taking the time to create the graphs to drive the point home.

… but you have Plots (dwelling ) as a hierarchy level in between (right diagram, below ), so you’ll never see the visit dates in the list view …

This is great to know! I’ve informed our stakeholders of the behaviour.

Unfortunately, at least from setting the permissions & counting flags, we did not see any change in ordering behaviour:

Thanks for trying all those options @Anro :pray:t4: !

This might be confirmed somewhere earlier in this chain, but I’d like to eliminate UHC stuff as a possibility for the unpredictable behavior. Can you please try…

  1. Removing “count_visits” from all contact types
  2. Making sure the users do not have can_view_last_visited_date
  3. Removing the uhc settings from your settings file

Do things behave as expected now?

Another question, how were you expecting to use UHC mode for CHWs, since they are only seeing Plots in their list view? For example… are you expecting to see the Last Visited date on the Plots and if so… how would you expect the Last Visited date on the Plot to be calculated - would it be the most recent Last Visited date across all the households in that Plot? How many households are typically in a Plot? And how would CHWs use information at the Plot level in their decision making?

@michael Unfortunately still no change this side.

  1. Removed count_visits from ALL contact types:
  2. Made sure NO users have can_view_last_visited_date
    image
  3. We never had any “uhc settings” set in the first place
  4. I even removed the can_view_uhc_stats just to be sure
    image

As can be seen below, the results are still the same:


Going off the code, the original decision to lean into that functionality was made quite a while ago, and as a result, the reasoning is somewhat foggy.
However, it was hoped that the visit count would be tallied for the households. The intention was to draw attention to less-visited households by placing them at the top of the list.

While reading the documentation again I investigated our app forms and found that none of them implement the visited_contact_uuid field. Thank you for drawing attention to it.

Given the lack of uhc settings, and our forms lacking the visited_contact_uuid field, leads me to believe that at the time the team did not really have a thorough understanding of the feature, or perhaps a solid spec/requirement list from our stakeholders, when this was implemented.


How many households are typically in a Plot?

That can differ greatly. A plot/dwelling/location by us is anything from a piece of land, to a block of flats. Which makes it quite a pain point to try and make sure the CHW enumerates all the inhabitable “households” in that place.

Gotcha. So it sounds like you don’t need UHC mode at all right now, and we’ve confirmed that it didn’t have any affect on the ordering anyway, so there is something else unrelated going on… I’ll need to ask an engineer to look into this.

In the meantime, one more question… do all CHWs experience the list the same way or does it depend on the user? For example… you have anro chw associated to Indawo. If you create another CHW called michael chw and associate a new user to michael chw with the exact same roles and parent, does that user see the exact same thing (Plot listed before Indawo). Also… I noticed that your Indawo does not have a “Primary Contact”. I don’t think it actually matters but I’ve started another thread just to confirm.

@Ben_Kiarie or @Jennifer_Quesada would you mind having a look at the code to see how/why items in the Contacts Page | List View are ordering unpredictably for Anro?

You’re on the money there. On our side the removal is busy going through review to get merged.
I’ve asked for smoke tests to be done just to make sure we’re seeing the same behavior everywhere. Just to be sure.
Thank you for continuing to look into this matter, and for pining Ben & Jennifer to assist.

Tested creating the michael chw

Thank you again for highlighting the conflict view!
We’ve run the command against our prod environment and seemingly we’re only able to get the count of affected records:

[
    {
        "id": null,
        "key": null,
        "value": 20,
        "doc": null
    }
]

It could be due to our python library used in notebooks that’s responsible for querying couchDB.

Are you able to explain the expected return structure of the conflict command?
Looking at the ddocs/medic-db/medic-conflicts/views/conflicts map file it seems like it could be a count or a doc:

function(doc) {
  if (doc._conflicts) {
    emit(doc._conflicts);
  }
}

Although running the command in terminal against a local instance produces the following result:

{
   "rows":[
      
   ]
}