diff --git a/setup.py b/setup.py index b934283..e23d073 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ setup( name="tc-core-analyzer-lib", - version="1.1.0", + version="1.2.0", author="Mohammad Amin Dadgar, TogetherCrew", maintainer="Mohammad Amin Dadgar", maintainer_email="dadgaramin96@gmail.com", diff --git a/tc_core_analyzer_lib/tests/integration/test_assess_mention.py b/tc_core_analyzer_lib/tests/integration/test_assess_mention.py new file mode 100644 index 0000000..b372707 --- /dev/null +++ b/tc_core_analyzer_lib/tests/integration/test_assess_mention.py @@ -0,0 +1,100 @@ +from unittest import TestCase + +import numpy as np +from tc_core_analyzer_lib.assess_engagement import EngagementAssessment +from tc_core_analyzer_lib.utils.activity import DiscordActivity + + +class TestAssessMentions(TestCase): + def setUp(self) -> None: + self.window_days = 7 + self.action_params = { + "INT_THR": 1, + "UW_DEG_THR": 1, + "PAUSED_T_THR": 1, + "CON_T_THR": 4, + "CON_O_THR": 3, + "EDGE_STR_THR": 5, + "UW_THR_DEG_THR": 5, + "VITAL_T_THR": 4, + "VITAL_O_THR": 3, + "STILL_T_THR": 2, + "STILL_O_THR": 2, + "DROP_H_THR": 2, + "DROP_I_THR": 1, + } + + def test_assess_single_reply(self): + acc_names = [] + acc_count = 5 + for i in range(5): + acc_names.append(f"user{i}") + + acc_names = np.array(acc_names) + + int_mat = { + DiscordActivity.Reply: np.zeros((acc_count, acc_count)), + DiscordActivity.Mention: np.zeros((acc_count, acc_count)), + DiscordActivity.Reaction: np.zeros((acc_count, acc_count)), + DiscordActivity.Lone_msg: np.zeros((acc_count, acc_count)), + DiscordActivity.Thread_msg: np.zeros((acc_count, acc_count)), + } + + # user0 mentioning user1 + int_mat[DiscordActivity.Mention][0, 1] = 1 + + activity_dict = { + "all_joined": {"0": set()}, + "all_joined_day": {"0": set()}, + "all_consistent": {}, + "all_vital": {}, + "all_active": {}, + "all_connected": {}, + "all_paused": {}, + "all_new_disengaged": {}, + "all_disengaged": {}, + "all_unpaused": {}, + "all_returned": {}, + "all_new_active": {}, + "all_still_active": {}, + "all_dropped": {}, + "all_disengaged_were_newly_active": {}, + "all_disengaged_were_consistently_active": {}, + "all_disengaged_were_vital": {}, + "all_lurker": {}, + "all_about_to_disengage": {}, + "all_disengaged_in_past": {}, + } + # window index + w_i = 0 + + activities = [ + DiscordActivity.Reaction, + DiscordActivity.Mention, + DiscordActivity.Reply, + DiscordActivity.Lone_msg, + DiscordActivity.Thread_msg, + ] + + engagement = EngagementAssessment( + activities=activities, + activities_ignore_0_axis=[ + DiscordActivity.Mention, + DiscordActivity.Reaction, + DiscordActivity.Reply, + ], + activities_ignore_1_axis=[], + ) + + (_, *computed_activities) = engagement.compute( + int_mat=int_mat, + w_i=w_i, + acc_names=acc_names, + act_param=self.action_params, + WINDOW_D=self.window_days, + **activity_dict, + ) + + computed_activities = dict(zip(activity_dict.keys(), computed_activities)) + + self.assertEqual(computed_activities["all_active"], {"0": set(["user0"])}) diff --git a/tc_core_analyzer_lib/tests/integration/test_assess_reactions.py b/tc_core_analyzer_lib/tests/integration/test_assess_reactions.py new file mode 100644 index 0000000..948239c --- /dev/null +++ b/tc_core_analyzer_lib/tests/integration/test_assess_reactions.py @@ -0,0 +1,100 @@ +from unittest import TestCase + +import numpy as np +from tc_core_analyzer_lib.assess_engagement import EngagementAssessment +from tc_core_analyzer_lib.utils.activity import DiscordActivity + + +class TestAssessReactions(TestCase): + def setUp(self) -> None: + self.window_days = 7 + self.action_params = { + "INT_THR": 1, + "UW_DEG_THR": 1, + "PAUSED_T_THR": 1, + "CON_T_THR": 4, + "CON_O_THR": 3, + "EDGE_STR_THR": 5, + "UW_THR_DEG_THR": 5, + "VITAL_T_THR": 4, + "VITAL_O_THR": 3, + "STILL_T_THR": 2, + "STILL_O_THR": 2, + "DROP_H_THR": 2, + "DROP_I_THR": 1, + } + + def test_assess_single_reaction(self): + acc_names = [] + acc_count = 5 + for i in range(5): + acc_names.append(f"user{i}") + + acc_names = np.array(acc_names) + + int_mat = { + DiscordActivity.Reply: np.zeros((acc_count, acc_count)), + DiscordActivity.Mention: np.zeros((acc_count, acc_count)), + DiscordActivity.Reaction: np.zeros((acc_count, acc_count)), + DiscordActivity.Lone_msg: np.zeros((acc_count, acc_count)), + DiscordActivity.Thread_msg: np.zeros((acc_count, acc_count)), + } + + # user0 reacting to user1 + int_mat[DiscordActivity.Reaction][0, 1] = 1 + + activity_dict = { + "all_joined": {"0": set()}, + "all_joined_day": {"0": set()}, + "all_consistent": {}, + "all_vital": {}, + "all_active": {}, + "all_connected": {}, + "all_paused": {}, + "all_new_disengaged": {}, + "all_disengaged": {}, + "all_unpaused": {}, + "all_returned": {}, + "all_new_active": {}, + "all_still_active": {}, + "all_dropped": {}, + "all_disengaged_were_newly_active": {}, + "all_disengaged_were_consistently_active": {}, + "all_disengaged_were_vital": {}, + "all_lurker": {}, + "all_about_to_disengage": {}, + "all_disengaged_in_past": {}, + } + # window index + w_i = 0 + + activities = [ + DiscordActivity.Reaction, + DiscordActivity.Mention, + DiscordActivity.Reply, + DiscordActivity.Lone_msg, + DiscordActivity.Thread_msg, + ] + + engagement = EngagementAssessment( + activities=activities, + activities_ignore_0_axis=[ + DiscordActivity.Mention, + DiscordActivity.Reaction, + DiscordActivity.Reply, + ], + activities_ignore_1_axis=[], + ) + + (_, *computed_activities) = engagement.compute( + int_mat=int_mat, + w_i=w_i, + acc_names=acc_names, + act_param=self.action_params, + WINDOW_D=self.window_days, + **activity_dict, + ) + + computed_activities = dict(zip(activity_dict.keys(), computed_activities)) + + self.assertEqual(computed_activities["all_active"], {"0": set(["user0"])}) diff --git a/tc_core_analyzer_lib/tests/integration/test_assess_replies.py b/tc_core_analyzer_lib/tests/integration/test_assess_replies.py new file mode 100644 index 0000000..a2752de --- /dev/null +++ b/tc_core_analyzer_lib/tests/integration/test_assess_replies.py @@ -0,0 +1,100 @@ +from unittest import TestCase + +import numpy as np +from tc_core_analyzer_lib.assess_engagement import EngagementAssessment +from tc_core_analyzer_lib.utils.activity import DiscordActivity + + +class TestAssessReplies(TestCase): + def setUp(self) -> None: + self.window_days = 7 + self.action_params = { + "INT_THR": 1, + "UW_DEG_THR": 1, + "PAUSED_T_THR": 1, + "CON_T_THR": 4, + "CON_O_THR": 3, + "EDGE_STR_THR": 5, + "UW_THR_DEG_THR": 5, + "VITAL_T_THR": 4, + "VITAL_O_THR": 3, + "STILL_T_THR": 2, + "STILL_O_THR": 2, + "DROP_H_THR": 2, + "DROP_I_THR": 1, + } + + def test_assess_single_reply(self): + acc_names = [] + acc_count = 5 + for i in range(5): + acc_names.append(f"user{i}") + + acc_names = np.array(acc_names) + + int_mat = { + DiscordActivity.Reply: np.zeros((acc_count, acc_count)), + DiscordActivity.Mention: np.zeros((acc_count, acc_count)), + DiscordActivity.Reaction: np.zeros((acc_count, acc_count)), + DiscordActivity.Lone_msg: np.zeros((acc_count, acc_count)), + DiscordActivity.Thread_msg: np.zeros((acc_count, acc_count)), + } + + # user0 replying user1 + int_mat[DiscordActivity.Reply][0, 1] = 1 + + activity_dict = { + "all_joined": {"0": set()}, + "all_joined_day": {"0": set()}, + "all_consistent": {}, + "all_vital": {}, + "all_active": {}, + "all_connected": {}, + "all_paused": {}, + "all_new_disengaged": {}, + "all_disengaged": {}, + "all_unpaused": {}, + "all_returned": {}, + "all_new_active": {}, + "all_still_active": {}, + "all_dropped": {}, + "all_disengaged_were_newly_active": {}, + "all_disengaged_were_consistently_active": {}, + "all_disengaged_were_vital": {}, + "all_lurker": {}, + "all_about_to_disengage": {}, + "all_disengaged_in_past": {}, + } + # window index + w_i = 0 + + activities = [ + DiscordActivity.Reaction, + DiscordActivity.Mention, + DiscordActivity.Reply, + DiscordActivity.Lone_msg, + DiscordActivity.Thread_msg, + ] + + engagement = EngagementAssessment( + activities=activities, + activities_ignore_0_axis=[ + DiscordActivity.Mention, + DiscordActivity.Reaction, + DiscordActivity.Reply, + ], + activities_ignore_1_axis=[], + ) + + (_, *computed_activities) = engagement.compute( + int_mat=int_mat, + w_i=w_i, + acc_names=acc_names, + act_param=self.action_params, + WINDOW_D=self.window_days, + **activity_dict, + ) + + computed_activities = dict(zip(activity_dict.keys(), computed_activities)) + + self.assertEqual(computed_activities["all_active"], {"0": set(["user0"])}) diff --git a/tc_core_analyzer_lib/tests/integration/test_lone_actions_all_active.py b/tc_core_analyzer_lib/tests/integration/test_lone_actions_all_active.py new file mode 100644 index 0000000..53cf033 --- /dev/null +++ b/tc_core_analyzer_lib/tests/integration/test_lone_actions_all_active.py @@ -0,0 +1,306 @@ +from unittest import TestCase + +import numpy as np +from tc_core_analyzer_lib.assess_engagement import EngagementAssessment +from tc_core_analyzer_lib.utils.activity import DiscordActivity + + +class TestActionsAllActive(TestCase): + def setUp(self) -> None: + self.window_days = 7 + self.action_params = { + "INT_THR": 1, + "UW_DEG_THR": 1, + "PAUSED_T_THR": 1, + "CON_T_THR": 4, + "CON_O_THR": 3, + "EDGE_STR_THR": 5, + "UW_THR_DEG_THR": 5, + "VITAL_T_THR": 4, + "VITAL_O_THR": 3, + "STILL_T_THR": 2, + "STILL_O_THR": 2, + "DROP_H_THR": 2, + "DROP_I_THR": 1, + } + + def test_no_actions(self): + acc_names = [] + acc_count = 5 + for i in range(5): + acc_names.append(f"user{i}") + + acc_names = np.array(acc_names) + + int_mat = { + DiscordActivity.Reply: np.zeros((acc_count, acc_count)), + DiscordActivity.Mention: np.zeros((acc_count, acc_count)), + DiscordActivity.Reaction: np.zeros((acc_count, acc_count)), + DiscordActivity.Lone_msg: np.zeros((acc_count, acc_count)), + DiscordActivity.Thread_msg: np.zeros((acc_count, acc_count)), + } + activity_dict = { + "all_joined": {"0": set()}, + "all_joined_day": {"0": set()}, + "all_consistent": {}, + "all_vital": {}, + "all_active": {}, + "all_connected": {}, + "all_paused": {}, + "all_new_disengaged": {}, + "all_disengaged": {}, + "all_unpaused": {}, + "all_returned": {}, + "all_new_active": {}, + "all_still_active": {}, + "all_dropped": {}, + "all_disengaged_were_newly_active": {}, + "all_disengaged_were_consistently_active": {}, + "all_disengaged_were_vital": {}, + "all_lurker": {}, + "all_about_to_disengage": {}, + "all_disengaged_in_past": {}, + } + # window index + w_i = 0 + + activities = [ + DiscordActivity.Reaction, + DiscordActivity.Mention, + DiscordActivity.Reply, + DiscordActivity.Lone_msg, + DiscordActivity.Thread_msg, + ] + + engagement = EngagementAssessment( + activities=activities, + activities_ignore_0_axis=[], + activities_ignore_1_axis=[], + ) + + (_, *computed_activities) = engagement.compute( + int_mat=int_mat, + w_i=w_i, + acc_names=acc_names, + act_param=self.action_params, + WINDOW_D=self.window_days, + **activity_dict, + ) + + computed_activities = dict(zip(activity_dict.keys(), computed_activities)) + + assert computed_activities["all_joined"] == {"0": set()} + assert computed_activities["all_consistent"] == {"0": set()} + assert computed_activities["all_vital"] == {"0": set()} + assert computed_activities["all_active"] == {"0": set()} + assert computed_activities["all_connected"] == {"0": set()} + assert computed_activities["all_dropped"] == {"0": set()} + assert computed_activities["all_new_active"] == {"0": set()} + assert computed_activities["all_still_active"] == {"0": set()} + assert computed_activities["all_paused"] == {"0": set()} + assert computed_activities["all_returned"] == {"0": set()} + assert computed_activities["all_unpaused"] == {"0": set()} + assert computed_activities["all_new_disengaged"] == {"0": set()} + assert computed_activities["all_disengaged"] == {"0": set()} + assert computed_activities["all_disengaged_were_newly_active"] == {"0": set()} + assert computed_activities["all_disengaged_were_consistently_active"] == { + "0": set() + } + assert computed_activities["all_disengaged_were_vital"] == {"0": set()} + assert computed_activities["all_lurker"] == {"0": set()} + assert computed_activities["all_about_to_disengage"] == {"0": set()} + assert computed_activities["all_disengaged_in_past"] == {"0": set()} + + def test_single_action_lone_msg(self): + acc_names = [] + acc_count = 5 + for i in range(5): + acc_names.append(f"user{i}") + + acc_names = np.array(acc_names) + + int_mat = { + DiscordActivity.Reply: np.zeros((acc_count, acc_count)), + DiscordActivity.Mention: np.zeros((acc_count, acc_count)), + DiscordActivity.Reaction: np.zeros((acc_count, acc_count)), + DiscordActivity.Lone_msg: np.zeros((acc_count, acc_count)), + DiscordActivity.Thread_msg: np.zeros((acc_count, acc_count)), + } + + int_mat[DiscordActivity.Lone_msg][1, 1] = 2 + + activity_dict = { + "all_joined": {"0": set()}, + "all_joined_day": {"0": set()}, + "all_consistent": {}, + "all_vital": {}, + "all_active": {}, + "all_connected": {}, + "all_paused": {}, + "all_new_disengaged": {}, + "all_disengaged": {}, + "all_unpaused": {}, + "all_returned": {}, + "all_new_active": {}, + "all_still_active": {}, + "all_dropped": {}, + "all_disengaged_were_newly_active": {}, + "all_disengaged_were_consistently_active": {}, + "all_disengaged_were_vital": {}, + "all_lurker": {}, + "all_about_to_disengage": {}, + "all_disengaged_in_past": {}, + } + # window index + w_i = 0 + + activities = [ + DiscordActivity.Reaction, + DiscordActivity.Mention, + DiscordActivity.Reply, + DiscordActivity.Lone_msg, + DiscordActivity.Thread_msg, + ] + + engagement = EngagementAssessment( + activities=activities, + activities_ignore_0_axis=[ + DiscordActivity.Mention, + DiscordActivity.Reaction, + DiscordActivity.Reply, + ], + activities_ignore_1_axis=[], + ) + + (_, *computed_activities) = engagement.compute( + int_mat=int_mat, + w_i=w_i, + acc_names=acc_names, + act_param=self.action_params, + WINDOW_D=self.window_days, + **activity_dict, + ) + + computed_activities = dict(zip(activity_dict.keys(), computed_activities)) + + assert computed_activities["all_joined"] == {"0": set()} + assert computed_activities["all_consistent"] == {"0": set()} + assert computed_activities["all_vital"] == {"0": set()} + assert computed_activities["all_active"] == {"0": set(["user1"])} + assert computed_activities["all_connected"] == {"0": set()} + assert computed_activities["all_dropped"] == {"0": set()} + assert computed_activities["all_new_active"] == {"0": set(["user1"])} + assert computed_activities["all_still_active"] == {"0": set()} + assert computed_activities["all_paused"] == {"0": set()} + assert computed_activities["all_returned"] == {"0": set()} + assert computed_activities["all_unpaused"] == {"0": set()} + assert computed_activities["all_new_disengaged"] == {"0": set()} + assert computed_activities["all_disengaged"] == {"0": set()} + assert computed_activities["all_disengaged_were_newly_active"] == {"0": set()} + assert computed_activities["all_disengaged_were_consistently_active"] == { + "0": set() + } + assert computed_activities["all_disengaged_were_vital"] == {"0": set()} + assert computed_activities["all_lurker"] == {"0": set()} + assert computed_activities["all_about_to_disengage"] == {"0": set()} + assert computed_activities["all_disengaged_in_past"] == {"0": set()} + + def test_multiple_actions_lone_msg(self): + acc_names = [] + acc_count = 5 + for i in range(5): + acc_names.append(f"user{i}") + + acc_names = np.array(acc_names) + + int_mat = { + DiscordActivity.Reply: np.zeros((acc_count, acc_count)), + DiscordActivity.Mention: np.zeros((acc_count, acc_count)), + DiscordActivity.Reaction: np.zeros((acc_count, acc_count)), + DiscordActivity.Lone_msg: np.zeros((acc_count, acc_count)), + DiscordActivity.Thread_msg: np.zeros((acc_count, acc_count)), + } + + int_mat[DiscordActivity.Lone_msg][1, 1] = 2 + int_mat[DiscordActivity.Lone_msg][2, 2] = 3 + int_mat[DiscordActivity.Lone_msg][3, 3] = 1 + + activity_dict = { + "all_joined": {"0": set()}, + "all_joined_day": {"0": set()}, + "all_consistent": {}, + "all_vital": {}, + "all_active": {}, + "all_connected": {}, + "all_paused": {}, + "all_new_disengaged": {}, + "all_disengaged": {}, + "all_unpaused": {}, + "all_returned": {}, + "all_new_active": {}, + "all_still_active": {}, + "all_dropped": {}, + "all_disengaged_were_newly_active": {}, + "all_disengaged_were_consistently_active": {}, + "all_disengaged_were_vital": {}, + "all_lurker": {}, + "all_about_to_disengage": {}, + "all_disengaged_in_past": {}, + } + # window index + w_i = 0 + + activities = [ + DiscordActivity.Reaction, + DiscordActivity.Mention, + DiscordActivity.Reply, + DiscordActivity.Lone_msg, + DiscordActivity.Thread_msg, + ] + + engagement = EngagementAssessment( + activities=activities, + activities_ignore_0_axis=[ + DiscordActivity.Mention, + DiscordActivity.Reaction, + DiscordActivity.Reply, + ], + activities_ignore_1_axis=[], + ) + + (_, *computed_activities) = engagement.compute( + int_mat=int_mat, + w_i=w_i, + acc_names=acc_names, + act_param=self.action_params, + WINDOW_D=self.window_days, + **activity_dict, + ) + + computed_activities = dict(zip(activity_dict.keys(), computed_activities)) + + assert computed_activities["all_joined"] == {"0": set()} + assert computed_activities["all_consistent"] == {"0": set()} + assert computed_activities["all_vital"] == {"0": set()} + assert computed_activities["all_active"] == { + "0": set(["user1", "user2", "user3"]) + } + assert computed_activities["all_connected"] == {"0": set()} + assert computed_activities["all_dropped"] == {"0": set()} + assert computed_activities["all_new_active"] == { + "0": set(["user1", "user2", "user3"]) + } + assert computed_activities["all_still_active"] == {"0": set()} + assert computed_activities["all_paused"] == {"0": set()} + assert computed_activities["all_returned"] == {"0": set()} + assert computed_activities["all_unpaused"] == {"0": set()} + assert computed_activities["all_new_disengaged"] == {"0": set()} + assert computed_activities["all_disengaged"] == {"0": set()} + assert computed_activities["all_disengaged_were_newly_active"] == {"0": set()} + assert computed_activities["all_disengaged_were_consistently_active"] == { + "0": set() + } + assert computed_activities["all_disengaged_were_vital"] == {"0": set()} + assert computed_activities["all_lurker"] == {"0": set()} + assert computed_activities["all_about_to_disengage"] == {"0": set()} + assert computed_activities["all_disengaged_in_past"] == {"0": set()} diff --git a/tc_core_analyzer_lib/tests/integration/test_lone_thr_actions_active.py b/tc_core_analyzer_lib/tests/integration/test_lone_thr_actions_active.py new file mode 100644 index 0000000..4dc49b7 --- /dev/null +++ b/tc_core_analyzer_lib/tests/integration/test_lone_thr_actions_active.py @@ -0,0 +1,128 @@ +from unittest import TestCase + +import numpy as np +from tc_core_analyzer_lib.assess_engagement import EngagementAssessment +from tc_core_analyzer_lib.utils.activity import DiscordActivity + + +class TestActionsAllActive(TestCase): + def setUp(self) -> None: + self.window_days = 7 + self.action_params = { + "INT_THR": 1, + "UW_DEG_THR": 1, + "PAUSED_T_THR": 1, + "CON_T_THR": 4, + "CON_O_THR": 3, + "EDGE_STR_THR": 5, + "UW_THR_DEG_THR": 5, + "VITAL_T_THR": 4, + "VITAL_O_THR": 3, + "STILL_T_THR": 2, + "STILL_O_THR": 2, + "DROP_H_THR": 2, + "DROP_I_THR": 1, + } + + def test_multiple_actions(self): + acc_names = [] + acc_count = 5 + for i in range(5): + acc_names.append(f"user{i}") + + acc_names = np.array(acc_names) + + int_mat = { + DiscordActivity.Reply: np.zeros((acc_count, acc_count)), + DiscordActivity.Mention: np.zeros((acc_count, acc_count)), + DiscordActivity.Reaction: np.zeros((acc_count, acc_count)), + DiscordActivity.Lone_msg: np.zeros((acc_count, acc_count)), + DiscordActivity.Thread_msg: np.zeros((acc_count, acc_count)), + } + + int_mat[DiscordActivity.Thread_msg][1, 1] = 2 + int_mat[DiscordActivity.Lone_msg][1, 1] = 6 + int_mat[DiscordActivity.Thread_msg][2, 2] = 3 + int_mat[DiscordActivity.Thread_msg][3, 3] = 1 + int_mat[DiscordActivity.Lone_msg][3, 3] = 1 + int_mat[DiscordActivity.Lone_msg][4, 4] = 1 + + activity_dict = { + "all_joined": {"0": set()}, + "all_joined_day": {"0": set()}, + "all_consistent": {}, + "all_vital": {}, + "all_active": {}, + "all_connected": {}, + "all_paused": {}, + "all_new_disengaged": {}, + "all_disengaged": {}, + "all_unpaused": {}, + "all_returned": {}, + "all_new_active": {}, + "all_still_active": {}, + "all_dropped": {}, + "all_disengaged_were_newly_active": {}, + "all_disengaged_were_consistently_active": {}, + "all_disengaged_were_vital": {}, + "all_lurker": {}, + "all_about_to_disengage": {}, + "all_disengaged_in_past": {}, + } + # window index + w_i = 0 + + activities = [ + DiscordActivity.Reaction, + DiscordActivity.Mention, + DiscordActivity.Reply, + DiscordActivity.Lone_msg, + DiscordActivity.Thread_msg, + ] + + engagement = EngagementAssessment( + activities=activities, + activities_ignore_0_axis=[ + DiscordActivity.Mention, + DiscordActivity.Reaction, + DiscordActivity.Reply, + ], + activities_ignore_1_axis=[], + ) + + (_, *computed_activities) = engagement.compute( + int_mat=int_mat, + w_i=w_i, + acc_names=acc_names, + act_param=self.action_params, + WINDOW_D=self.window_days, + **activity_dict, + ) + + computed_activities = dict(zip(activity_dict.keys(), computed_activities)) + + assert computed_activities["all_joined"] == {"0": set()} + assert computed_activities["all_consistent"] == {"0": set()} + assert computed_activities["all_vital"] == {"0": set()} + assert computed_activities["all_active"] == { + "0": set(["user1", "user2", "user3", "user4"]) + } + assert computed_activities["all_connected"] == {"0": set()} + assert computed_activities["all_dropped"] == {"0": set()} + assert computed_activities["all_new_active"] == { + "0": set(["user1", "user2", "user3", "user4"]) + } + assert computed_activities["all_still_active"] == {"0": set()} + assert computed_activities["all_paused"] == {"0": set()} + assert computed_activities["all_returned"] == {"0": set()} + assert computed_activities["all_unpaused"] == {"0": set()} + assert computed_activities["all_new_disengaged"] == {"0": set()} + assert computed_activities["all_disengaged"] == {"0": set()} + assert computed_activities["all_disengaged_were_newly_active"] == {"0": set()} + assert computed_activities["all_disengaged_were_consistently_active"] == { + "0": set() + } + assert computed_activities["all_disengaged_were_vital"] == {"0": set()} + assert computed_activities["all_lurker"] == {"0": set()} + assert computed_activities["all_about_to_disengage"] == {"0": set()} + assert computed_activities["all_disengaged_in_past"] == {"0": set()} diff --git a/tc_core_analyzer_lib/tests/integration/test_thr_actions_all_active.py b/tc_core_analyzer_lib/tests/integration/test_thr_actions_all_active.py new file mode 100644 index 0000000..93db632 --- /dev/null +++ b/tc_core_analyzer_lib/tests/integration/test_thr_actions_all_active.py @@ -0,0 +1,313 @@ +from unittest import TestCase + +import numpy as np +from tc_core_analyzer_lib.assess_engagement import EngagementAssessment +from tc_core_analyzer_lib.utils.activity import DiscordActivity + + +class TestActionsAllActive(TestCase): + def setUp(self) -> None: + self.window_days = 7 + self.action_params = { + "INT_THR": 1, + "UW_DEG_THR": 1, + "PAUSED_T_THR": 1, + "CON_T_THR": 4, + "CON_O_THR": 3, + "EDGE_STR_THR": 5, + "UW_THR_DEG_THR": 5, + "VITAL_T_THR": 4, + "VITAL_O_THR": 3, + "STILL_T_THR": 2, + "STILL_O_THR": 2, + "DROP_H_THR": 2, + "DROP_I_THR": 1, + } + + def test_single_action_thr_msg(self): + acc_names = [] + acc_count = 5 + for i in range(5): + acc_names.append(f"user{i}") + + acc_names = np.array(acc_names) + + int_mat = { + DiscordActivity.Reply: np.zeros((acc_count, acc_count)), + DiscordActivity.Mention: np.zeros((acc_count, acc_count)), + DiscordActivity.Reaction: np.zeros((acc_count, acc_count)), + DiscordActivity.Lone_msg: np.zeros((acc_count, acc_count)), + DiscordActivity.Thread_msg: np.zeros((acc_count, acc_count)), + } + + int_mat[DiscordActivity.Thread_msg][1, 1] = 2 + + activity_dict = { + "all_joined": {"0": set()}, + "all_joined_day": {"0": set()}, + "all_consistent": {}, + "all_vital": {}, + "all_active": {}, + "all_connected": {}, + "all_paused": {}, + "all_new_disengaged": {}, + "all_disengaged": {}, + "all_unpaused": {}, + "all_returned": {}, + "all_new_active": {}, + "all_still_active": {}, + "all_dropped": {}, + "all_disengaged_were_newly_active": {}, + "all_disengaged_were_consistently_active": {}, + "all_disengaged_were_vital": {}, + "all_lurker": {}, + "all_about_to_disengage": {}, + "all_disengaged_in_past": {}, + } + # window index + w_i = 0 + + activities = [ + DiscordActivity.Reaction, + DiscordActivity.Mention, + DiscordActivity.Reply, + DiscordActivity.Lone_msg, + DiscordActivity.Thread_msg, + ] + + engagement = EngagementAssessment( + activities=activities, + activities_ignore_0_axis=[ + DiscordActivity.Mention, + DiscordActivity.Reaction, + DiscordActivity.Reply, + ], + activities_ignore_1_axis=[], + ) + + (_, *computed_activities) = engagement.compute( + int_mat=int_mat, + w_i=w_i, + acc_names=acc_names, + act_param=self.action_params, + WINDOW_D=self.window_days, + **activity_dict, + ) + + computed_activities = dict(zip(activity_dict.keys(), computed_activities)) + + assert computed_activities["all_joined"] == {"0": set()} + assert computed_activities["all_consistent"] == {"0": set()} + assert computed_activities["all_vital"] == {"0": set()} + assert computed_activities["all_active"] == {"0": set(["user1"])} + assert computed_activities["all_connected"] == {"0": set()} + assert computed_activities["all_dropped"] == {"0": set()} + assert computed_activities["all_new_active"] == {"0": set(["user1"])} + assert computed_activities["all_still_active"] == {"0": set()} + assert computed_activities["all_paused"] == {"0": set()} + assert computed_activities["all_returned"] == {"0": set()} + assert computed_activities["all_unpaused"] == {"0": set()} + assert computed_activities["all_new_disengaged"] == {"0": set()} + assert computed_activities["all_disengaged"] == {"0": set()} + assert computed_activities["all_disengaged_were_newly_active"] == {"0": set()} + assert computed_activities["all_disengaged_were_consistently_active"] == { + "0": set() + } + assert computed_activities["all_disengaged_were_vital"] == {"0": set()} + assert computed_activities["all_lurker"] == {"0": set()} + assert computed_activities["all_about_to_disengage"] == {"0": set()} + assert computed_activities["all_disengaged_in_past"] == {"0": set()} + + def test_multiple_actions_thread_msg(self): + acc_names = [] + acc_count = 5 + for i in range(5): + acc_names.append(f"user{i}") + + acc_names = np.array(acc_names) + + int_mat = { + DiscordActivity.Reply: np.zeros((acc_count, acc_count)), + DiscordActivity.Mention: np.zeros((acc_count, acc_count)), + DiscordActivity.Reaction: np.zeros((acc_count, acc_count)), + DiscordActivity.Lone_msg: np.zeros((acc_count, acc_count)), + DiscordActivity.Thread_msg: np.zeros((acc_count, acc_count)), + } + + int_mat[DiscordActivity.Thread_msg][1, 1] = 2 + int_mat[DiscordActivity.Thread_msg][2, 2] = 3 + int_mat[DiscordActivity.Thread_msg][3, 3] = 1 + + activity_dict = { + "all_joined": {"0": set()}, + "all_joined_day": {"0": set()}, + "all_consistent": {}, + "all_vital": {}, + "all_active": {}, + "all_connected": {}, + "all_paused": {}, + "all_new_disengaged": {}, + "all_disengaged": {}, + "all_unpaused": {}, + "all_returned": {}, + "all_new_active": {}, + "all_still_active": {}, + "all_dropped": {}, + "all_disengaged_were_newly_active": {}, + "all_disengaged_were_consistently_active": {}, + "all_disengaged_were_vital": {}, + "all_lurker": {}, + "all_about_to_disengage": {}, + "all_disengaged_in_past": {}, + } + # window index + w_i = 0 + + activities = [ + DiscordActivity.Reaction, + DiscordActivity.Mention, + DiscordActivity.Reply, + DiscordActivity.Lone_msg, + DiscordActivity.Thread_msg, + ] + + engagement = EngagementAssessment( + activities=activities, + activities_ignore_0_axis=[ + DiscordActivity.Mention, + DiscordActivity.Reaction, + DiscordActivity.Reply, + ], + activities_ignore_1_axis=[], + ) + + (_, *computed_activities) = engagement.compute( + int_mat=int_mat, + w_i=w_i, + acc_names=acc_names, + act_param=self.action_params, + WINDOW_D=self.window_days, + **activity_dict, + ) + + computed_activities = dict(zip(activity_dict.keys(), computed_activities)) + + assert computed_activities["all_joined"] == {"0": set()} + assert computed_activities["all_consistent"] == {"0": set()} + assert computed_activities["all_vital"] == {"0": set()} + assert computed_activities["all_active"] == { + "0": set(["user1", "user2", "user3"]) + } + assert computed_activities["all_connected"] == {"0": set()} + assert computed_activities["all_dropped"] == {"0": set()} + assert computed_activities["all_new_active"] == { + "0": set(["user1", "user2", "user3"]) + } + assert computed_activities["all_still_active"] == {"0": set()} + assert computed_activities["all_paused"] == {"0": set()} + assert computed_activities["all_returned"] == {"0": set()} + assert computed_activities["all_unpaused"] == {"0": set()} + assert computed_activities["all_new_disengaged"] == {"0": set()} + assert computed_activities["all_disengaged"] == {"0": set()} + assert computed_activities["all_disengaged_were_newly_active"] == {"0": set()} + assert computed_activities["all_disengaged_were_consistently_active"] == { + "0": set() + } + assert computed_activities["all_disengaged_were_vital"] == {"0": set()} + assert computed_activities["all_lurker"] == {"0": set()} + assert computed_activities["all_about_to_disengage"] == {"0": set()} + assert computed_activities["all_disengaged_in_past"] == {"0": set()} + + def test_single_action_thread_msg(self): + acc_names = [] + acc_count = 5 + for i in range(5): + acc_names.append(f"user{i}") + + acc_names = np.array(acc_names) + + int_mat = { + DiscordActivity.Reply: np.zeros((acc_count, acc_count)), + DiscordActivity.Mention: np.zeros((acc_count, acc_count)), + DiscordActivity.Reaction: np.zeros((acc_count, acc_count)), + DiscordActivity.Lone_msg: np.zeros((acc_count, acc_count)), + DiscordActivity.Thread_msg: np.zeros((acc_count, acc_count)), + } + + int_mat[DiscordActivity.Thread_msg][1, 1] = 2 + + activity_dict = { + "all_joined": {"0": set()}, + "all_joined_day": {"0": set()}, + "all_consistent": {}, + "all_vital": {}, + "all_active": {}, + "all_connected": {}, + "all_paused": {}, + "all_new_disengaged": {}, + "all_disengaged": {}, + "all_unpaused": {}, + "all_returned": {}, + "all_new_active": {}, + "all_still_active": {}, + "all_dropped": {}, + "all_disengaged_were_newly_active": {}, + "all_disengaged_were_consistently_active": {}, + "all_disengaged_were_vital": {}, + "all_lurker": {}, + "all_about_to_disengage": {}, + "all_disengaged_in_past": {}, + } + # window index + w_i = 0 + + activities = [ + DiscordActivity.Reaction, + DiscordActivity.Mention, + DiscordActivity.Reply, + DiscordActivity.Lone_msg, + DiscordActivity.Thread_msg, + ] + + engagement = EngagementAssessment( + activities=activities, + activities_ignore_0_axis=[ + DiscordActivity.Mention, + DiscordActivity.Reaction, + DiscordActivity.Reply, + ], + activities_ignore_1_axis=[], + ) + + (_, *computed_activities) = engagement.compute( + int_mat=int_mat, + w_i=w_i, + acc_names=acc_names, + act_param=self.action_params, + WINDOW_D=self.window_days, + **activity_dict, + ) + + computed_activities = dict(zip(activity_dict.keys(), computed_activities)) + + assert computed_activities["all_joined"] == {"0": set()} + assert computed_activities["all_consistent"] == {"0": set()} + assert computed_activities["all_vital"] == {"0": set()} + assert computed_activities["all_active"] == {"0": set(["user1"])} + assert computed_activities["all_connected"] == {"0": set()} + assert computed_activities["all_dropped"] == {"0": set()} + assert computed_activities["all_new_active"] == {"0": set(["user1"])} + assert computed_activities["all_still_active"] == {"0": set()} + assert computed_activities["all_paused"] == {"0": set()} + assert computed_activities["all_returned"] == {"0": set()} + assert computed_activities["all_unpaused"] == {"0": set()} + assert computed_activities["all_new_disengaged"] == {"0": set()} + assert computed_activities["all_disengaged"] == {"0": set()} + assert computed_activities["all_disengaged_were_newly_active"] == {"0": set()} + assert computed_activities["all_disengaged_were_consistently_active"] == { + "0": set() + } + assert computed_activities["all_disengaged_were_vital"] == {"0": set()} + assert computed_activities["all_lurker"] == {"0": set()} + assert computed_activities["all_about_to_disengage"] == {"0": set()} + assert computed_activities["all_disengaged_in_past"] == {"0": set()} diff --git a/tc_core_analyzer_lib/utils/activity.py b/tc_core_analyzer_lib/utils/activity.py index 69e95b5..051f11d 100644 --- a/tc_core_analyzer_lib/utils/activity.py +++ b/tc_core_analyzer_lib/utils/activity.py @@ -5,6 +5,7 @@ class BaseActivity: Reply: str = "reply" Mention: str = "mention" + Reaction: str = "reaction" class DiscordActivity: @@ -14,4 +15,6 @@ class DiscordActivity: Reply: str = BaseActivity.Reply Mention: str = BaseActivity.Mention - Reaction: str = "reaction" + Reaction: str = BaseActivity.Reaction + Thread_msg: str = "thr_message" + Lone_msg: str = "lone_message" diff --git a/tc_core_analyzer_lib/utils/compute_interaction_per_acc.py b/tc_core_analyzer_lib/utils/compute_interaction_per_acc.py index 2e98249..a86bbdc 100644 --- a/tc_core_analyzer_lib/utils/compute_interaction_per_acc.py +++ b/tc_core_analyzer_lib/utils/compute_interaction_per_acc.py @@ -59,19 +59,10 @@ def thr_int( graph : networkx.DiGraph the network graph of active members """ + ignore_axis_0_activities: list[str] = kwargs.get("ignore_axis_0_activities", []) + ignore_axis_1_activities: list[str] = kwargs.get("ignore_axis_1_activities", []) - ignore_axis_0_activities: list[str] - ignore_axis_1_activities: list[str] - - if "ignore_axis_0_activities" not in kwargs: - ignore_axis_0_activities = [] - else: - ignore_axis_0_activities = kwargs["ignore_axis_0_activities"] - if "ignore_axis_1_activities" not in kwargs: - ignore_axis_1_activities = [] - else: - ignore_axis_1_activities = kwargs["ignore_axis_1_activities"] - + # int_analysis is for all actions and interactions int_analysis = get_analysis_vector( int_mat=int_mat, activites=activities, @@ -85,6 +76,7 @@ def thr_int( matrix = np.zeros_like(int_mat[activities[0]]) for activity in activities: + # matrix for action and interactions matrix += int_mat[activity] graph = make_graph(matrix) @@ -97,7 +89,7 @@ def thr_int( # # # TOTAL CONNECTIONS # # # # get unweighted node degree value for each node - all_degrees = np.array([val for (node, val) in graph.degree()]) + all_degrees = np.array([val for (_, val) in graph.degree()]) # compare total unweighted node degree to interaction threshold thr_uw_deg = np.where(all_degrees >= UW_DEG_THR)[0] @@ -116,8 +108,14 @@ def thr_int( ] ) - # get unweighted node degree value for each node from thresholded network - all_degrees_thresh = np.array([val for (node, val) in thresh_graph.degree()]) + # preparing matrix with no `action` and just interactions + # actions were self-intereaction and are on diagonal + matrix_interaction = copy.deepcopy(matrix) + matrix_interaction[np.diag_indices_from(matrix_interaction)] = 0 + graph_interaction = make_graph(matrix_interaction) + + # get unweighted node degree value for each node from interaction network + all_degrees_thresh = np.array([val for (_, val) in graph_interaction.degree()]) # compare total unweighted node degree after thresholding to threshold thr_uw_thr_deg = np.where(all_degrees_thresh > UW_THR_DEG_THR)[0]