From 237ba5fc2db675fca7920108dd2139a4947dda3b Mon Sep 17 00:00:00 2001 From: Mohammad Amin Date: Tue, 5 Mar 2024 17:01:22 +0330 Subject: [PATCH 1/7] feat: Added two more actions to discord activity! --- setup.py | 2 +- tc_core_analyzer_lib/utils/activity.py | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) 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/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" From d96921e0a92afa8df5c6c20ff551bc3707f7febd Mon Sep 17 00:00:00 2001 From: Mohammad Amin Date: Tue, 5 Mar 2024 17:46:59 +0330 Subject: [PATCH 2/7] feat: including the actions for activity! --- .../utils/compute_interaction_per_acc.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) 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..9d05ec3 100644 --- a/tc_core_analyzer_lib/utils/compute_interaction_per_acc.py +++ b/tc_core_analyzer_lib/utils/compute_interaction_per_acc.py @@ -5,6 +5,7 @@ from networkx import DiGraph from .generate_graph import make_graph +from utils.activity import DiscordActivity def thr_int( @@ -72,6 +73,7 @@ def thr_int( 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, @@ -83,10 +85,16 @@ def thr_int( # all the activities has the same interaction matrix # with the same shape matrix = np.zeros_like(int_mat[activities[0]]) + interaction_matrix = np.zeros_like(int_mat[activities[0]]) for activity in activities: + if activity in [DiscordActivity.Reaction, DiscordActivity.Mention, DiscordActivity.Reaction]: + interaction_matrix += int_mat[activity] + + # matrix for action and interactions matrix += int_mat[activity] + interaction_graph = make_graph(interaction_matrix) graph = make_graph(matrix) # # # TOTAL INTERACTIONS # # # @@ -97,27 +105,24 @@ 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] # # # THRESHOLDED CONNECTIONS # # # - # make copy of graph for thresholding - thresh_graph = copy.deepcopy(graph) - # remove edges below threshold from copy - thresh_graph.remove_edges_from( + interaction_graph.remove_edges_from( [ (n1, n2) - for n1, n2, w in thresh_graph.edges(data="weight") + for n1, n2, w in interaction_graph.edges(data="weight") if w < EDGE_STR_THR ] ) # get unweighted node degree value for each node from thresholded network - all_degrees_thresh = np.array([val for (node, val) in thresh_graph.degree()]) + all_degrees_thresh = np.array([val for (_, val) in interaction_graph.degree()]) # compare total unweighted node degree after thresholding to threshold thr_uw_thr_deg = np.where(all_degrees_thresh > UW_THR_DEG_THR)[0] From 3a916a78b2e570b259d4cb4c5c4b4ffcc88cc41b Mon Sep 17 00:00:00 2001 From: Mohammad Amin Date: Tue, 5 Mar 2024 17:49:59 +0330 Subject: [PATCH 3/7] feat: fix import error! --- tc_core_analyzer_lib/utils/compute_interaction_per_acc.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) 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 9d05ec3..2c72c52 100644 --- a/tc_core_analyzer_lib/utils/compute_interaction_per_acc.py +++ b/tc_core_analyzer_lib/utils/compute_interaction_per_acc.py @@ -1,11 +1,9 @@ -import copy - import numpy as np import numpy.typing as npt from networkx import DiGraph from .generate_graph import make_graph -from utils.activity import DiscordActivity +from .activity import DiscordActivity def thr_int( From 0c13206aa292c5286cb28d1cd3271028de27f469 Mon Sep 17 00:00:00 2001 From: Mohammad Amin Date: Tue, 5 Mar 2024 18:32:38 +0330 Subject: [PATCH 4/7] update: interaction and actions compute method! --- .../utils/compute_interaction_per_acc.py | 39 ++++++++----------- 1 file changed, 17 insertions(+), 22 deletions(-) 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 2c72c52..15b1aca 100644 --- a/tc_core_analyzer_lib/utils/compute_interaction_per_acc.py +++ b/tc_core_analyzer_lib/utils/compute_interaction_per_acc.py @@ -1,9 +1,10 @@ +import copy + import numpy as np import numpy.typing as npt from networkx import DiGraph from .generate_graph import make_graph -from .activity import DiscordActivity def thr_int( @@ -58,18 +59,8 @@ def thr_int( graph : networkx.DiGraph the network graph of active members """ - - 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"] + ignore_axis_0_activities: list[str] = kwargs.get("ignore_axis_0_activities", []) + ignore_axis_1_activities: list[str] = kwargs.get("ignore_axis_1_activities", []) # int_analysis is for all actions and interactions int_analysis = get_analysis_vector( @@ -83,16 +74,11 @@ def thr_int( # all the activities has the same interaction matrix # with the same shape matrix = np.zeros_like(int_mat[activities[0]]) - interaction_matrix = np.zeros_like(int_mat[activities[0]]) for activity in activities: - if activity in [DiscordActivity.Reaction, DiscordActivity.Mention, DiscordActivity.Reaction]: - interaction_matrix += int_mat[activity] - # matrix for action and interactions matrix += int_mat[activity] - interaction_graph = make_graph(interaction_matrix) graph = make_graph(matrix) # # # TOTAL INTERACTIONS # # # @@ -110,17 +96,26 @@ def thr_int( # # # THRESHOLDED CONNECTIONS # # # + # make copy of graph for thresholding + thresh_graph = copy.deepcopy(graph) + # remove edges below threshold from copy - interaction_graph.remove_edges_from( + thresh_graph.remove_edges_from( [ (n1, n2) - for n1, n2, w in interaction_graph.edges(data="weight") + for n1, n2, w in thresh_graph.edges(data="weight") if w < EDGE_STR_THR ] ) - # get unweighted node degree value for each node from thresholded network - all_degrees_thresh = np.array([val for (_, val) in interaction_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] From c713abe9acef57950194a4ce8cb8b079f7c69328 Mon Sep 17 00:00:00 2001 From: Mohammad Amin Date: Tue, 5 Mar 2024 18:39:12 +0330 Subject: [PATCH 5/7] fix: wrong function call! --- tc_core_analyzer_lib/utils/compute_interaction_per_acc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 15b1aca..a86bbdc 100644 --- a/tc_core_analyzer_lib/utils/compute_interaction_per_acc.py +++ b/tc_core_analyzer_lib/utils/compute_interaction_per_acc.py @@ -111,7 +111,7 @@ def thr_int( # 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 + 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 From dc76942ca70e12e5bb21f959d7319ecd252270f3 Mon Sep 17 00:00:00 2001 From: Mohammad Amin Date: Wed, 6 Mar 2024 07:29:49 +0330 Subject: [PATCH 6/7] feat: Added test case for actions! we had test case for interactions, we're now adding some for just actions. --- .../test_lone_actions_all_active.py | 306 +++++++++++++++++ .../test_lone_thr_actions_active.py | 128 +++++++ .../test_thr_actions_all_active.py | 313 ++++++++++++++++++ 3 files changed, 747 insertions(+) create mode 100644 tc_core_analyzer_lib/tests/integration/test_lone_actions_all_active.py create mode 100644 tc_core_analyzer_lib/tests/integration/test_lone_thr_actions_active.py create mode 100644 tc_core_analyzer_lib/tests/integration/test_thr_actions_all_active.py 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()} From bfbac012c36b895c6cd6131ed0b5bfbd73b3d47c Mon Sep 17 00:00:00 2001 From: Mohammad Amin Date: Wed, 6 Mar 2024 14:02:27 +0330 Subject: [PATCH 7/7] feat: Added more test cases to ensure everything's working properly! --- .../tests/integration/test_assess_mention.py | 100 ++++++++++++++++++ .../integration/test_assess_reactions.py | 100 ++++++++++++++++++ .../tests/integration/test_assess_replies.py | 100 ++++++++++++++++++ 3 files changed, 300 insertions(+) create mode 100644 tc_core_analyzer_lib/tests/integration/test_assess_mention.py create mode 100644 tc_core_analyzer_lib/tests/integration/test_assess_reactions.py create mode 100644 tc_core_analyzer_lib/tests/integration/test_assess_replies.py 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"])})