move-coop / parsons

A python library of connectors for the progressive community.
https://www.parsonsproject.org/
Other
261 stars 132 forks source link

NGPVAN -- update_person_json function doesn't update person record #673

Open harnoorSingh-wtpmi opened 2 years ago

harnoorSingh-wtpmi commented 2 years ago

The update_person_json() function does not seem to function as the documentation indicates. When I use the function to attempt to update a contact (in the demo EA site using a sandbox the EA team setup for me) in the following way:

van = VAN(db='MyCampaign') van.update_person_json(id=101991312, match_json={'codes':None})

I get a strange JSON output that is completely different from the actual person's record (it is shown below)

{'vanId': 101991312,
 'firstName': 'joe',
 'lastName': 'jack',
 'middleName': None,
 'suffix': None,
 'title': None,
 'sourceFileTitle': None,
 'sourceFileFirstName': None,
 'sourceFileMiddleName': None,
 'sourceFileLastName': None,
 'sourceFileSuffix': None,
 'contactMode': None,
 'organizationContactCommonName': None,
 'organizationContactOfficialName': None,
 'salutation': 'joe',
 'formalSalutation': 'joe jack',
 'additionalSalutation': None,
 'preferredPronoun': None,
 'pronouns': None,
 'envelopeName': 'joe jack',
 'formalEnvelopeName': 'joe jack',
 'additionalEnvelopeName': None,
 'contactMethodPreferenceCode': None,
 'nickname': None,
 'website': None,
 'professionalSuffix': None,
 'party': None,
 'employer': None,
 'occupation': None,
 'jobTitle': None,
 'sex': None,
 'dateOfBirth': None,
 'selfReportedRace': None,
 'selfReportedEthnicity': None,
 'selfReportedRaces': None,
 'selfReportedEthnicities': None,
 'selfReportedGenders': None,
 'selfReportedSexualOrientations': None,
 'selfReportedLanguagePreference': None,
 'emails': None,
 'phones': None,
 'addresses': None,
 'recordedAddresses': None,
 'identifiers': None,
 'codes': None,
 'customFields': None,
 'primaryCustomField': None,
 'contributionSummary': None,
 'suppressions': None,
 'caseworkCases': None,
 'caseworkIssues': None,
 'caseworkStories': None,
 'notes': None,
 'scores': None,
 'customProperties': None,
 'electionRecords': None,
 'membershipStatus': None,
 'organizationRoles': None,
 'districts': None,
 'surveyQuestionResponses': None,
 'finderNumber': None,
 'biographyImageUrl': None,
 'primaryContact': None}

But if I run

van.get_person(id=101991312)

I get the following which proves that the above JSON is not reflecting the updated record of the person

people INFO Getting person with  of 101991312 at url people/101991312

{'vanId': 101991312,
 'firstName': 'joe',
 'lastName': 'jack',
 'middleName': None,
 'suffix': None,
 'title': None,
 'sourceFileTitle': None,
 'sourceFileFirstName': None,
 'sourceFileMiddleName': None,
 'sourceFileLastName': None,
 'sourceFileSuffix': None,
 'contactMode': None,
 'organizationContactCommonName': None,
 'organizationContactOfficialName': None,
 'salutation': 'joe',
 'formalSalutation': 'joe jack',
 'additionalSalutation': None,
 'preferredPronoun': None,
 'pronouns': None,
 'envelopeName': 'joe jack',
 'formalEnvelopeName': 'joe jack',
 'additionalEnvelopeName': None,
 'contactMethodPreferenceCode': None,
 'nickname': None,
 'website': None,
 'professionalSuffix': None,
 'party': None,
 'employer': None,
 'occupation': None,
 'jobTitle': None,
 'sex': None,
 'dateOfBirth': None,
 'selfReportedRace': None,
 'selfReportedEthnicity': None,
 'selfReportedRaces': None,
 'selfReportedEthnicities': None,
 'selfReportedGenders': None,
 'selfReportedSexualOrientations': None,
 'selfReportedLanguagePreference': None,
 'emails': [{'type': 'P',
   'email': 'kate+test@sink.sendgrid.net',
   'dateCreated': '2022-04-11T10:39:00Z',
   'isPreferred': True,
   'isSubscribed': True,
   'subscriptionStatus': 'S'}],
 'phones': [],
 'addresses': [],
 'recordedAddresses': [],
 'identifiers': [],
 'codes': [{'codeId': 1002611,
   'parentCodeId': None,
   'name': 'test source code',
   'codeType': 'SourceCode'}],
 'customFields': [],
 'primaryCustomField': None,
 'contributionSummary': None,
 'suppressions': [],
 'caseworkCases': [],
 'caseworkIssues': [],
 'caseworkStories': [],
 'notes': [],
 'scores': None,
 'customProperties': [],
 'electionRecords': [],
 'membershipStatus': None,
 'organizationRoles': [],
 'districts': [{'districtFieldId': 0,
   'name': 'State',
   'parentFieldId': None,
   'isCustomDistrict': False,
   'districtFieldValues': [{'id': 'FL',
     'name': 'Florida',
     'parentId': None}]}],
 'surveyQuestionResponses': None,
 'finderNumber': None,
 'biographyImageUrl': None,
 'primaryContact': None}

When I walked through this issue with Shauna from the Parsons team she pointed out that this command doesn't appear to hit an endpoint for the API that would delete or update a contact's record according to Parson's documentation for the update_person_json() method. It appears that the documentation is incorrect or this function is not working exactly as it should.

Just for reference, this is the API's page of documentation that provides code which successfully (I have tested it and it works) deletes codes off of contacts' records, and this is the [page of the documentation]( for the API that is linked for the update_person_json() parson method.

thebbennett commented 2 years ago

Hey! I'm also working on a script that uses this function. Can you tell me what the expected output is? I've used this function in previous scripts at Sunrise with success

harnoorSingh-wtpmi commented 2 years ago

So from my understanding of the documentation, I would think that the function should edit the person's record with whatever has been provided to match_json that differs from the current record. As for the output I'm a little less sure, but again, from the documentation I would guess that it should be the new JSON record of the person after it has been updated according to the match_json field. In my case this would be a JSON field like this

{'vanId': 101991312,
 'firstName': 'joe',
 'lastName': 'jack',
 'middleName': None,
 'suffix': None,
 'title': None,
 'sourceFileTitle': None,
 'sourceFileFirstName': None,
 'sourceFileMiddleName': None,
 'sourceFileLastName': None,
 'sourceFileSuffix': None,
 'contactMode': None,
 'organizationContactCommonName': None,
 'organizationContactOfficialName': None,
 'salutation': 'joe',
 'formalSalutation': 'joe jack',
 'additionalSalutation': None,
 'preferredPronoun': None,
 'pronouns': None,
 'envelopeName': 'joe jack',
 'formalEnvelopeName': 'joe jack',
 'additionalEnvelopeName': None,
 'contactMethodPreferenceCode': None,
 'nickname': None,
 'website': None,
 'professionalSuffix': None,
 'party': None,
 'employer': None,
 'occupation': None,
 'jobTitle': None,
 'sex': None,
 'dateOfBirth': None,
 'selfReportedRace': None,
 'selfReportedEthnicity': None,
 'selfReportedRaces': None,
 'selfReportedEthnicities': None,
 'selfReportedGenders': None,
 'selfReportedSexualOrientations': None,
 'selfReportedLanguagePreference': None,
 'emails': [{'type': 'P',
   'email': 'kate+test@sink.sendgrid.net',
   'dateCreated': '2022-04-11T10:39:00Z',
   'isPreferred': True,
   'isSubscribed': True,
   'subscriptionStatus': 'S'}],
 'phones': [],
 'addresses': [],
 'recordedAddresses': [],
 'identifiers': [],
 'codes': [{'codeId': 1002611,
   'parentCodeId': None,
   'name': 'test source code',
   'codeType': 'SourceCode'}],
 'customFields': [],
 'primaryCustomField': None,
 'contributionSummary': None,
 'suppressions': [],
 'caseworkCases': [],
 'caseworkIssues': [],
 'caseworkStories': [],
 'notes': [],
 'scores': None,
 'customProperties': [],
 'electionRecords': [],
 'membershipStatus': None,
 'organizationRoles': [],
 'districts': [{'districtFieldId': 0,
   'name': 'State',
   'parentFieldId': None,
   'isCustomDistrict': False,
   'districtFieldValues': [{'id': 'FL',
     'name': 'Florida',
     'parentId': None}]}],
 'surveyQuestionResponses': None,
 'finderNumber': None,
 'biographyImageUrl': None,
 'primaryContact': None}

(the codes field now is blank and all codes have been wiped). Let me know if any of my understanding around this function is off.

I'm also a little confused because it seems that the update_person_json function is hitting this endpoint with the POST method, but this doesn't seem to allow for any updating or deleting of information. I hope all of that helps!

thebbennett commented 2 years ago

This is what my code looks like

json = {"phones": [{"phoneNumber": phone, "phoneOptInStatus": "I"}]}  # I for Opted In
r = ea.update_person_json(id=vanid, match_json=json)

I'm going to play around and see if I can verify that I'm updating the record

harnoorSingh-wtpmi commented 2 years ago

Alright, thank you, please let me know!

thebbennett commented 2 years ago

I attempted to opt myself in and out of our SMS list and when I checked my record, my PhoneOptInStatus was still "Unknown". This is odd because I worked with TMC to build this script a year or so ago and was under the impression that it worked.

harnoorSingh-wtpmi commented 2 years ago

Yes, this is essentially the problem I'm having. It's very odd because the endpoint should allow you to update a record if you provide an existing VANID, but that doesn't appear to be working for any field based on your example. So now I'm not sure if it's an issue with Parsons or the endpoint itself.

ydamit commented 2 years ago

Hi folks,

TL;DR: I think there might not actually be a bug here.

I wanted to report that I was able to update a phone's opt in using @thebbennett's exact code. I even tested it with a stringified phone number, and even with dashes, suggesting that EA strips way non-digits in the API call to the people/{vanid} endpoint. I think the response JSON from EA must be set up to automatically not expand any of the array items (see the GET people/{vanid} endpoint endpoint's $expand param for my reference point here). But I saw the changes in the UI pretty much instantly.

So with that in mind, along with other recent API changes, I can confirm that I was also unable to add or remove Codes using the upsert_person_json() method, nor, crucially, with POSTing to the person/{vanid} endpoint at all. I was, however, able to both add and remove Codes by POSTing to the person/{vanid}/codes endpoint and DELETEing at the person/{vanid}/codes/{codeid} endpoint, respectively. Considering that Codes is also absent from the Common Model example, I suspect that EA was allowing changes using the old, catch-all endpoint for a while, but have since deprecated that as an option.

I think, then, the problem may just be that we have no ability to ask for expanded arrays in responses from the people/{vanid} endpoints, and that the people/{vanid}/codes endpoints don't respond with anything! As a result, I was only able to confirm successes in the UI, or by making a GET people/{vanid} request (which was the only way for Source Codes, since they don't show up in the UI), and not in the response.

shaunagm commented 2 years ago

I haven't had a chance to look into this too deeply, but the get_person method, which update_person and update_person_json call via the intermediary of _people_search, has a bug in it which messes up the expand_fields parameters and was causing 404s for me. Might be related?

sduveen-pivot commented 1 year ago

I think this issue is just that the relationships are not updateable from the update_person endpoint. We have two paths we could go depending on how general-case we want to do:

  1. error out if someone tries to change relation fields (like codes) so people know they can't update those with this call
  2. upon seeing 'codes' in match_json, we instead call the codes endpoint as mentioned above and do that part for someone.

I think adding/deleting array/list objects might be frought -- e.g. if you think you're 'adding a code' by doing a for-loop on a bunch of IDs and codes=[{'codeId': 1002611....}] then you might not realize that you're actually removing all other codes from those users -- so I suggest we error out instead and provide separate api to remove_person_code()