From 397f2858f80236f90d2634220aaa25079f279df7 Mon Sep 17 00:00:00 2001 From: Yotam Date: Thu, 14 Sep 2023 08:13:01 -0400 Subject: [PATCH 1/7] include remove_tagging method --- parsons/action_builder/action_builder.py | 73 ++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/parsons/action_builder/action_builder.py b/parsons/action_builder/action_builder.py index 43fd70f1a6..fc24b7a7a9 100644 --- a/parsons/action_builder/action_builder.py +++ b/parsons/action_builder/action_builder.py @@ -289,6 +289,79 @@ def add_section_field_values_to_record( identifier=identifier, data=data, campaign=campaign ) + def remove_tagging( + self, identifier=None, tag_id=None, tag_name=None, tagging_id=None, + campaign=None + ): + """ + Remove one or more tags (i.e. custom field value) from an existing entity or connection + record in Action Builder. The basis for this end point is the combination of the tag's + interact ID and that of the specific tagging. The tag ID can usually be determined from + the tag's name, and the tagging ID can be derived if the identifier of the entity or + connection record is supplied instead. + `Args:` + identifier: str + Optional. The unique identifier for an entity or connection record being updated. + If omitted, `tagging_id` must be provided. + tag_id: str + Optional. The unique identifier for the tag being removed. If omitted, `tag_name` + must be provided. + tag_name: str + Optional. The exact name of the tag being removed. May result in an error if + multiple tags (in different fields/sections) have the same name. If omitted, + `tag_id` must be provided. + tagging_id: str + Optional. The unique identifier for the specific application of the tag to an + individual entity or connection record. If omitted, `identifier` must be provided. + campaign: str + Optional. The 36-character "interact ID" of the campaign whose data is to be + retrieved or edited. Not necessary if supplied when instantiating the class. + `Returns:` + API response JSON which contains `{'message': 'Tag has been removed from Taggable + Logbook'}` if successful. + """ + + if {tag_name, tag_id} == {None}: + raise ValueError("Please supply a tag_name or tag_id!") + + if {identifier, tagging_id}: + raise ValueError( + "Please supply an entity or connection identifier, or a tagging id!") + + campaign = self._campaign_check(campaign) + + if tag_name and {tag_id, tagging_id} == {None}: + + tag_data = self.get_tag_by_name(tag_name, campaign=campaign) + tag_count = tag_data.num_rows + + if tag_count > 1: + error_msg = f"Found {tag_count} tags with this name. " + error_msg += "Try with using the unique interact ID" + raise ValueError(error_msg) + + tag_id = tag_data['identifiers'][0][0].split(':')[1] + logger.info(f"Tag {tag_name} has ID {tag_id}") + + if tagging_id and not tag_id: + raise ValueError("Cannot search based on tagging ID alone.") + + if tag_id and not tagging_id: + + endpoint = f"tags/{tag_id}/taggings" + taggings = self._get_all_records(self.campaign, endpoint) + taggings_filtered = taggings.select_rows( + lambda row: + identifier in row['_links']['action_builder:connection']['href'] + if row['item_type'] == 'connection' + else identifier in row['osdi:person']['href'] + ) + tagging_id = [x.split(':')[1] for x in taggings_filtered['identifiers'][0] + if 'action_builder' in x][0] + + logger.info(f"Removing tag {tag_id} from {identifier or tagging_id}") + return self.api.delete_request(f'campaigns/{campaign}/{endpoint}/{tagging_id}') + def upsert_connection(self, identifiers, tag_data=None, campaign=None): """ Load or update a connection record in Action Builder between two existing entity records. From b2cd4efa8ce412c644eb0955c7b9295d30046f5c Mon Sep 17 00:00:00 2001 From: Yotam Date: Thu, 14 Sep 2023 08:20:53 -0400 Subject: [PATCH 2/7] complete logic for error catching --- parsons/action_builder/action_builder.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parsons/action_builder/action_builder.py b/parsons/action_builder/action_builder.py index fc24b7a7a9..50879b2ac6 100644 --- a/parsons/action_builder/action_builder.py +++ b/parsons/action_builder/action_builder.py @@ -324,7 +324,7 @@ def remove_tagging( if {tag_name, tag_id} == {None}: raise ValueError("Please supply a tag_name or tag_id!") - if {identifier, tagging_id}: + if {identifier, tagging_id} == {None}: raise ValueError( "Please supply an entity or connection identifier, or a tagging id!") From 58a57026f046720a2a63de4a24d513f0fe2bdce8 Mon Sep 17 00:00:00 2001 From: Yotam Date: Thu, 14 Sep 2023 08:31:54 -0400 Subject: [PATCH 3/7] test remove_tag method --- test/test_action_builder/test_action_builder.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test/test_action_builder/test_action_builder.py b/test/test_action_builder/test_action_builder.py index 79b1ae89bf..080f4e588d 100644 --- a/test/test_action_builder/test_action_builder.py +++ b/test/test_action_builder/test_action_builder.py @@ -207,6 +207,12 @@ def setUp(self, m): f"action_builder:{self.fake_entity_id}" ] + self.fake_tag_id = 'fake_tag_id' + self.fake_tagging_id = 'fake_tagging_id' + self.fake_remove_tag_resp = { + "message": "Tag has been removed from Taggable Logbook" + } + self.fake_connection = {"person_id": "fake-entity-id-2"} @requests_mock.Mocker() @@ -362,6 +368,17 @@ def test_add_section_field_values_to_record(self, m): ) self.assertEqual(add_tags_response, self.fake_tagging) + @requests_mock.Mocker() + def test_remove_tagging(self, m): + m.delete( + f"{self.api_url}/tags/{self.fake_tag_id}/taggings/{self.fake_tagging_id}", + json=self.fake_remove_tag_resp + ) + remove_tag_resp = self.bldr.remove_tagging( + tag_id=self.fake_tag_id, tagging_id=self.fake_tagging_id + ) + self.assertEqual(remove_tag_resp, self.fake_tagging_id) + def connect_callback(self, request, context): # Internal method for returning constructed connection data to test From 49b89074451f432c7beea015918c501509e64669 Mon Sep 17 00:00:00 2001 From: Yotam Date: Thu, 14 Sep 2023 08:34:30 -0400 Subject: [PATCH 4/7] build endpoint based on tag ID --- parsons/action_builder/action_builder.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/parsons/action_builder/action_builder.py b/parsons/action_builder/action_builder.py index 50879b2ac6..4bf5e9a9ad 100644 --- a/parsons/action_builder/action_builder.py +++ b/parsons/action_builder/action_builder.py @@ -329,6 +329,7 @@ def remove_tagging( "Please supply an entity or connection identifier, or a tagging id!") campaign = self._campaign_check(campaign) + endpoint = "tags/{}/taggings" if tag_name and {tag_id, tagging_id} == {None}: @@ -348,8 +349,7 @@ def remove_tagging( if tag_id and not tagging_id: - endpoint = f"tags/{tag_id}/taggings" - taggings = self._get_all_records(self.campaign, endpoint) + taggings = self._get_all_records(self.campaign, endpoint.format(tag_id)) taggings_filtered = taggings.select_rows( lambda row: identifier in row['_links']['action_builder:connection']['href'] @@ -360,7 +360,8 @@ def remove_tagging( if 'action_builder' in x][0] logger.info(f"Removing tag {tag_id} from {identifier or tagging_id}") - return self.api.delete_request(f'campaigns/{campaign}/{endpoint}/{tagging_id}') + return self.api.delete_request( + f'campaigns/{campaign}/{endpoint.format(tag_id)}/{tagging_id}') def upsert_connection(self, identifiers, tag_data=None, campaign=None): """ From 5e12a1240d704c6325e83f19a060757a499a60f3 Mon Sep 17 00:00:00 2001 From: Yotam Date: Thu, 14 Sep 2023 08:35:29 -0400 Subject: [PATCH 5/7] correct comparison in test --- test/test_action_builder/test_action_builder.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_action_builder/test_action_builder.py b/test/test_action_builder/test_action_builder.py index 080f4e588d..c86c47e00f 100644 --- a/test/test_action_builder/test_action_builder.py +++ b/test/test_action_builder/test_action_builder.py @@ -377,7 +377,7 @@ def test_remove_tagging(self, m): remove_tag_resp = self.bldr.remove_tagging( tag_id=self.fake_tag_id, tagging_id=self.fake_tagging_id ) - self.assertEqual(remove_tag_resp, self.fake_tagging_id) + self.assertEqual(remove_tag_resp, self.fake_remove_tag_resp) def connect_callback(self, request, context): # Internal method for returning constructed connection data to test From 8006a86436c81cded2ff575ef0b4d42dd213f310 Mon Sep 17 00:00:00 2001 From: Yotam Date: Thu, 14 Sep 2023 08:37:37 -0400 Subject: [PATCH 6/7] lint remove tag method --- parsons/action_builder/action_builder.py | 67 +++++++++++++----------- 1 file changed, 37 insertions(+), 30 deletions(-) diff --git a/parsons/action_builder/action_builder.py b/parsons/action_builder/action_builder.py index 4bf5e9a9ad..da6fb7c59b 100644 --- a/parsons/action_builder/action_builder.py +++ b/parsons/action_builder/action_builder.py @@ -290,49 +290,53 @@ def add_section_field_values_to_record( ) def remove_tagging( - self, identifier=None, tag_id=None, tag_name=None, tagging_id=None, - campaign=None + self, + identifier=None, + tag_id=None, + tag_name=None, + tagging_id=None, + campaign=None, ): """ - Remove one or more tags (i.e. custom field value) from an existing entity or connection - record in Action Builder. The basis for this end point is the combination of the tag's - interact ID and that of the specific tagging. The tag ID can usually be determined from - the tag's name, and the tagging ID can be derived if the identifier of the entity or + Remove one or more tags (i.e. custom field value) from an existing entity or connection + record in Action Builder. The basis for this end point is the combination of the tag's + interact ID and that of the specific tagging. The tag ID can usually be determined from + the tag's name, and the tagging ID can be derived if the identifier of the entity or connection record is supplied instead. `Args:` identifier: str - Optional. The unique identifier for an entity or connection record being updated. + Optional. The unique identifier for an entity or connection record being updated. If omitted, `tagging_id` must be provided. tag_id: str - Optional. The unique identifier for the tag being removed. If omitted, `tag_name` + Optional. The unique identifier for the tag being removed. If omitted, `tag_name` must be provided. tag_name: str - Optional. The exact name of the tag being removed. May result in an error if - multiple tags (in different fields/sections) have the same name. If omitted, + Optional. The exact name of the tag being removed. May result in an error if + multiple tags (in different fields/sections) have the same name. If omitted, `tag_id` must be provided. tagging_id: str - Optional. The unique identifier for the specific application of the tag to an + Optional. The unique identifier for the specific application of the tag to an individual entity or connection record. If omitted, `identifier` must be provided. campaign: str Optional. The 36-character "interact ID" of the campaign whose data is to be retrieved or edited. Not necessary if supplied when instantiating the class. `Returns:` - API response JSON which contains `{'message': 'Tag has been removed from Taggable + API response JSON which contains `{'message': 'Tag has been removed from Taggable Logbook'}` if successful. """ - + if {tag_name, tag_id} == {None}: raise ValueError("Please supply a tag_name or tag_id!") - + if {identifier, tagging_id} == {None}: raise ValueError( - "Please supply an entity or connection identifier, or a tagging id!") - + "Please supply an entity or connection identifier, or a tagging id!" + ) + campaign = self._campaign_check(campaign) endpoint = "tags/{}/taggings" - - if tag_name and {tag_id, tagging_id} == {None}: + if tag_name and {tag_id, tagging_id} == {None}: tag_data = self.get_tag_by_name(tag_name, campaign=campaign) tag_count = tag_data.num_rows @@ -341,27 +345,30 @@ def remove_tagging( error_msg += "Try with using the unique interact ID" raise ValueError(error_msg) - tag_id = tag_data['identifiers'][0][0].split(':')[1] + tag_id = tag_data["identifiers"][0][0].split(":")[1] logger.info(f"Tag {tag_name} has ID {tag_id}") - + if tagging_id and not tag_id: raise ValueError("Cannot search based on tagging ID alone.") - - if tag_id and not tagging_id: + if tag_id and not tagging_id: taggings = self._get_all_records(self.campaign, endpoint.format(tag_id)) taggings_filtered = taggings.select_rows( - lambda row: - identifier in row['_links']['action_builder:connection']['href'] - if row['item_type'] == 'connection' - else identifier in row['osdi:person']['href'] + lambda row: identifier + in row["_links"]["action_builder:connection"]["href"] + if row["item_type"] == "connection" + else identifier in row["osdi:person"]["href"] ) - tagging_id = [x.split(':')[1] for x in taggings_filtered['identifiers'][0] - if 'action_builder' in x][0] - + tagging_id = [ + x.split(":")[1] + for x in taggings_filtered["identifiers"][0] + if "action_builder" in x + ][0] + logger.info(f"Removing tag {tag_id} from {identifier or tagging_id}") return self.api.delete_request( - f'campaigns/{campaign}/{endpoint.format(tag_id)}/{tagging_id}') + f"campaigns/{campaign}/{endpoint.format(tag_id)}/{tagging_id}" + ) def upsert_connection(self, identifiers, tag_data=None, campaign=None): """ From 2432ba75be6db29d97f0bed0e3092a2a8b07a148 Mon Sep 17 00:00:00 2001 From: Yotam Date: Thu, 14 Sep 2023 08:43:26 -0400 Subject: [PATCH 7/7] lint testing file --- test/test_action_builder/test_action_builder.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/test_action_builder/test_action_builder.py b/test/test_action_builder/test_action_builder.py index c86c47e00f..57fcdfcb45 100644 --- a/test/test_action_builder/test_action_builder.py +++ b/test/test_action_builder/test_action_builder.py @@ -207,8 +207,8 @@ def setUp(self, m): f"action_builder:{self.fake_entity_id}" ] - self.fake_tag_id = 'fake_tag_id' - self.fake_tagging_id = 'fake_tagging_id' + self.fake_tag_id = "fake_tag_id" + self.fake_tagging_id = "fake_tagging_id" self.fake_remove_tag_resp = { "message": "Tag has been removed from Taggable Logbook" } @@ -372,7 +372,7 @@ def test_add_section_field_values_to_record(self, m): def test_remove_tagging(self, m): m.delete( f"{self.api_url}/tags/{self.fake_tag_id}/taggings/{self.fake_tagging_id}", - json=self.fake_remove_tag_resp + json=self.fake_remove_tag_resp, ) remove_tag_resp = self.bldr.remove_tagging( tag_id=self.fake_tag_id, tagging_id=self.fake_tagging_id