77
88import pandas as pd
99import requests
10- from loguru import logger
1110
1211
1312def parse_command_line_args () -> argparse .Namespace :
@@ -48,7 +47,7 @@ def passing_samples(df: pd.DataFrame, coverage_threshold) -> tuple[str, int]:
4847 for i in range (len (df .iloc [:, 1 ])):
4948 proportion = df .iloc [i , 1 ]
5049 sample_id = df .iloc [i , 0 ]
51- threshold = 1 / coverage_threshold
50+ threshold = 1 - ( 1 / coverage_threshold )
5251 if proportion >= threshold :
5352 count += 1
5453 passing_line = f"{ sample_id } : { proportion } \n "
@@ -62,20 +61,19 @@ def failing_samples(df: pd.DataFrame, coverage_threshold) -> str:
6261 for i in range (len (df .iloc [:, 1 ])):
6362 proportion = df .iloc [i , 1 ]
6463 sample_id = df .iloc [i , 0 ]
65- threshold = 1 / coverage_threshold
64+ threshold = 1 - ( 1 / coverage_threshold )
6665 if proportion < threshold :
6766 failing_line = f"{ sample_id } : { proportion } \n "
6867 failing_message += failing_line
6968 return failing_message
7069
7170
72- def get_webhook_paths () -> list [str ]:
73- """Resolve list of webhook URLs from env or default file."""
71+ def get_user_ids () -> list [str ]:
7472 # Check ONEROOF_SLACK_HOOKS environment variable
75- path_str = os .environ .get ("ONEROOF_SLACK_HOOKS " )
73+ path_str = os .environ .get ("ONEROOF_SLACK_USER_IDS " )
7674
7775 path = (
78- Path .home () / ".oneroof" / "slack.webhooks " if not path_str else Path (path_str )
76+ Path .home () / ".oneroof" / "slack.user_ids " if not path_str else Path (path_str )
7977 )
8078
8179 # If the file doesn't exist or is empty, return empty list
@@ -87,23 +85,29 @@ def get_webhook_paths() -> list[str]:
8785 return [line .strip () for line in f if line .strip () and not line .startswith ("#" )]
8886
8987
88+ def get_slack_token () -> str :
89+ path_str = os .environ .get ("ONEROOF_SLACK_TOKEN" )
90+
91+ path = Path .home () / ".oneroof" / "slack.token" if not path_str else Path (path_str )
92+
93+ if not path .exists () or not path .is_file ():
94+ return ""
95+
96+ # Return the first non-empty line, stripped
97+ with path .open () as f :
98+ for line in f :
99+ if line .strip ():
100+ return line .strip ()
101+ return "" # In case the file is empty
102+
103+
90104def send_slack_notification (
91105 run_label : str ,
92106 stats_tsv : Path | str ,
93107 coverage_threshold : int ,
94108) -> None :
95- # the webhook url
96- # reading the tsv
97- # print(stats_tsv)
98109 stats_df = pd .read_csv (stats_tsv , sep = "\t " )
99110
100- # getting the webhooks
101- webhook_urls = get_webhook_paths ()
102-
103- if not webhook_urls :
104- logger .error ("No webhook URLs found. Exiting." )
105- return
106-
107111 # finding passing and failing
108112 passing , count_passing = passing_samples (stats_df , coverage_threshold )
109113 failing = failing_samples (stats_df , coverage_threshold )
@@ -119,15 +123,25 @@ def send_slack_notification(
119123
120124 complete_message = f"{ message } \n ```{ results } ```"
121125
122- payload = {"text" : complete_message }
123- for slack_webhook_url in webhook_urls :
124- # TODO(@akalweit5): Add reasonable timeout and consider retry strategy
125- r = requests .post (slack_webhook_url , json = payload )
126- if (r .status_code ) != 200 : # noqa: PLR2004
127- msg = f"Error sending slack automation, response code: { r .status_code } "
128- # TODO(@akalweit5): Find a better exception here. What actually is the error we're expecting? And could we just use
129- # an assert somewhere to crash early?
130- raise Exception (msg )
126+ user_id_list = get_user_ids ()
127+ slack_token = get_slack_token ()
128+
129+ for user_id in user_id_list :
130+ resp = requests .post (
131+ "https://slack.com/api/conversations.open" ,
132+ headers = {"Authorization" : f"Bearer { slack_token } " },
133+ json = {"users" : user_id },
134+ )
135+ channel_id = resp .json ().get ("channel" , {}).get ("id" )
136+ if not channel_id :
137+ raise RuntimeError ("Failed to open conversation." )
138+
139+ # Send the message
140+ msg_resp = requests .post (
141+ "https://slack.com/api/chat.postMessage" ,
142+ headers = {"Authorization" : f"Bearer { slack_token } " },
143+ json = {"channel" : channel_id , "text" : complete_message },
144+ )
131145
132146
133147def main () -> None :
0 commit comments