Skip to content

Commit 0a27e2a

Browse files
authored
VAN Bulk Import Additions (move-coop#1078)
* van bulk import - add apply canvass results & custom contact fields / docstring fixes / clarification / contact mapping * van bulk import additions - fix E501
1 parent 231d875 commit 0a27e2a

File tree

2 files changed

+186
-7
lines changed

2 files changed

+186
-7
lines changed

parsons/ngpvan/bulk_import.py

+147-7
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,9 @@ def bulk_apply_activist_codes(self, tbl, url_type, **url_kwargs):
189189
* - ``datecanvassed``
190190
- No
191191
- An ISO formatted date
192+
* - ``canvassedby``
193+
- No
194+
- A valid User ID; Required when DateCanvassed is provided
192195
* - ``contacttypeid``
193196
- No
194197
- The method of contact.
@@ -249,7 +252,7 @@ def bulk_upsert_contacts(self, tbl, url_type, result_fields=None, **url_kwargs):
249252
-
250253
-
251254
* - First Name
252-
- ``fn``, ``firstname``, ``last``
255+
- ``fn``, ``firstname``, ``first``
253256
-
254257
* - Middle Name
255258
- ``mn``, ``middlename``, ``middle``
@@ -258,8 +261,8 @@ def bulk_upsert_contacts(self, tbl, url_type, result_fields=None, **url_kwargs):
258261
- ``ln``, ``lastname``, ``last``
259262
-
260263
* - Date of Birth
261-
- ``dob``, ``dateofbirth`` ``birthdate``
262-
- What type of thing does this need?
264+
- ``dob``, ``dateofbirth``, ``birthdate``
265+
- An ISO formatted date
263266
* - Sex
264267
- ``sex``, ``gender``
265268
-
@@ -281,6 +284,9 @@ def bulk_upsert_contacts(self, tbl, url_type, result_fields=None, **url_kwargs):
281284
* - State Or Province
282285
- ``state``, ``st``, ``stateorprovince``
283286
-
287+
* - Zip or Postal Code
288+
- ``ziporpostal``, ``postal``, ``postalcode``, ``zip``, ``zipcode``
289+
-
284290
* - Country Code
285291
- ``countrycode``, ``country``
286292
- A valid two character country code (e.g. ``US``)
@@ -309,6 +315,9 @@ def bulk_upsert_contacts(self, tbl, url_type, result_fields=None, **url_kwargs):
309315
* - Email
310316
- ``email``, ``emailaddress``
311317
-
318+
* - Other Email
319+
- ``otheremail``, ``email2``, ``emailaddress2``
320+
-
312321
313322
`Args:`
314323
table: Parsons table
@@ -325,7 +334,7 @@ def bulk_upsert_contacts(self, tbl, url_type, result_fields=None, **url_kwargs):
325334
The bulk import job id
326335
"""
327336

328-
tbl = tbl.map_columns(COLUMN_MAP, exact_match=False)
337+
tbl = tbl.map_columns(CONTACTS_COLUMN_MAP, exact_match=False)
329338

330339
return self.post_bulk_import(
331340
tbl,
@@ -339,13 +348,13 @@ def bulk_upsert_contacts(self, tbl, url_type, result_fields=None, **url_kwargs):
339348

340349
def bulk_apply_suppressions(self, tbl, url_type, **url_kwargs):
341350
"""
342-
Bulk apply contact suppressions codes.
351+
Bulk apply contact suppression codes.
343352
344353
The table may include the following columns. The first column
345354
must be ``vanid``.
346355
347356
.. list-table::
348-
:widths: 25 25
357+
:widths: 25 25 50
349358
:header-rows: 1
350359
351360
* - Column Name
@@ -381,11 +390,140 @@ def bulk_apply_suppressions(self, tbl, url_type, **url_kwargs):
381390
**url_kwargs,
382391
)
383392

393+
def bulk_apply_canvass_results(self, tbl, url_type, **url_kwargs):
394+
"""
395+
Bulk apply contact canvass results.
396+
397+
The table may include the following columns. The first column
398+
must be ``vanid``.
399+
400+
.. list-table::
401+
:widths: 25 25 50
402+
:header-rows: 1
403+
404+
* - Column Name
405+
- Required
406+
- Description
407+
* - ``vanid``
408+
- Yes
409+
- A valid VANID primary key
410+
* - ``contacttypeid``
411+
- Yes
412+
- Valid Contact Type ID
413+
* - ``resultid``
414+
- Yes
415+
- Valid Contact Result ID
416+
* - ``datecanvassed``
417+
- Yes
418+
- ISO Date Format
419+
* - ``canvassedby``
420+
- Yes
421+
- Valid User ID
422+
* - ``phone``
423+
- No
424+
- Attempted Phone Number
425+
* - ``countrycode``
426+
- No
427+
- Country Code (ISO 3166-1 alpha-2)
428+
* - ``phonetypeid``
429+
- No
430+
- Phone Type
431+
* - ``phoneoptinstatusid``
432+
- No
433+
- SMS Opt-In Status
434+
* - ``addressid``
435+
- No
436+
- The Contact Address ID of the address that was canvassed
437+
438+
`Args:`
439+
table: Parsons table
440+
A Parsons table.
441+
url_type: str
442+
The cloud file storage to use to post the file (``S3`` or ``GCS``).
443+
See :ref:`Cloud Storage <cloud-storage>` for more details.
444+
**url_kwargs: kwargs
445+
Arguments to configure your cloud storage url type. See
446+
:ref:`Cloud Storage <cloud-storage>` for more details.
447+
`Returns:`
448+
int
449+
The bulk import job id
450+
"""
451+
452+
return self.post_bulk_import(
453+
tbl,
454+
url_type,
455+
"Contacts",
456+
[{"name": "CanvassResults"}],
457+
"Apply Canvass Results",
458+
**url_kwargs,
459+
)
460+
461+
def bulk_apply_contact_custom_fields(self, custom_field_group_id, tbl, url_type, **url_kwargs):
462+
"""
463+
Bulk apply contact custom fields.
464+
465+
The table may include the following columns. The first column
466+
must be ``vanid``.
467+
468+
.. list-table::
469+
:widths: 25 25 60
470+
:header-rows: 1
471+
472+
* - Column Name
473+
- Required
474+
- Description
475+
* - ``vanid``
476+
- Yes
477+
- A valid VANID primary key
478+
* - ***``CF{CustomFieldID}``
479+
- Yes
480+
- At least one custom field column to be loaded associated with the provided
481+
custom_field_group_id. The column name should be a valid Custom Field ID
482+
prefixed with ``CF``, i.e. CF123.
483+
484+
`Args:`
485+
custom_field_group_id: int
486+
Valid Custom Contact Field Group ID; must be the parent of
487+
the provided Custom Field IDs in the file.
488+
table: Parsons table
489+
A Parsons table.
490+
url_type: str
491+
The cloud file storage to use to post the file (``S3`` or ``GCS``).
492+
See :ref:`Cloud Storage <cloud-storage>` for more details.
493+
**url_kwargs: kwargs
494+
Arguments to configure your cloud storage url type. See
495+
:ref:`Cloud Storage <cloud-storage>` for more details.
496+
`Returns:`
497+
int
498+
The bulk import job id
499+
"""
500+
501+
mapping_types = [
502+
{
503+
"name": "ApplyContactCustomFields",
504+
"fieldValueMappings": [
505+
{
506+
"fieldName": "CustomFieldGroupID",
507+
"staticValue": custom_field_group_id,
508+
},
509+
],
510+
}
511+
]
512+
513+
return self.post_bulk_import(
514+
tbl,
515+
url_type,
516+
"Contacts",
517+
mapping_types,
518+
"Apply Contact Custom Fields",
519+
**url_kwargs,
520+
)
521+
384522

385523
# This is a column mapper that is used to accept additional column names and provide
386524
# flexibility for the user.
387525

388-
COLUMN_MAP = {
526+
CONTACTS_COLUMN_MAP = {
389527
"firstname": ["fn", "first"],
390528
"middlename": ["mn", "middle"],
391529
"lastname": ["ln", "last"],
@@ -396,11 +534,13 @@ def bulk_apply_suppressions(self, tbl, url_type, **url_kwargs):
396534
"addressline3": ["addressline3", "address3"],
397535
"city": [],
398536
"stateorprovince": ["state", "st"],
537+
"ziporpostal": ["postal", "postalcode", "zip", "zipcode"],
399538
"countrycode": ["country"],
400539
"displayasentered": [],
401540
"cellphone": ["cell"],
402541
"cellphonecountrycode": ["cellcountrycode"],
403542
"phone": ["home", "homephone"],
404543
"phonecountrycode": ["phonecountrycode"],
405544
"email": ["emailaddress"],
545+
"otheremail": ["email2", "emailaddress2"],
406546
}

test/test_van/test_bulkimport.py

+39
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,45 @@ def test_bulk_upsert_contacts(self, m):
164164

165165
self.assertEqual(job_id, 54679)
166166

167+
@requests_mock.Mocker()
168+
def test_bulk_apply_canvass_results(self, m):
169+
170+
# Mock Cloud Storage
171+
cloud_storage.post_file = mock.MagicMock()
172+
cloud_storage.post_file.return_value = "https://s3.com/my_file.zip"
173+
174+
tbl = Table(
175+
[
176+
["vanid", "contacttypeid", "resultid", "datecanvassed", "canvassedby", "phone"],
177+
[1234, 1, 1, "2020-01-01", 987, "5554443210"],
178+
]
179+
)
180+
181+
m.post(self.van.connection.uri + "bulkImportJobs", json={"jobId": 54679})
182+
183+
job_id = self.van.bulk_apply_canvass_results(tbl, url_type="S3", bucket="my-bucket")
184+
185+
self.assertEqual(job_id, 54679)
186+
187+
@requests_mock.Mocker()
188+
def test_bulk_apply_contact_custom_fields(self, m):
189+
190+
# Mock Cloud Storage
191+
cloud_storage.post_file = mock.MagicMock()
192+
cloud_storage.post_file.return_value = "https://s3.com/my_file.zip"
193+
194+
tbl = Table([["vanid", "CF123", "CF124"], [1234, "Test String Value", 999]])
195+
196+
m.post(self.van.connection.uri + "bulkImportJobs", json={"jobId": 54679})
197+
198+
custom_field_group_id = 1234
199+
200+
job_id = self.van.bulk_apply_contact_custom_fields(
201+
custom_field_group_id, tbl, url_type="S3", bucket="my-bucket"
202+
)
203+
204+
self.assertEqual(job_id, 54679)
205+
167206

168207
mapping_type = {
169208
"name": "ActivistCode",

0 commit comments

Comments
 (0)