Skip to content

Commit cf808d7

Browse files
committed
Merge branch 'dev'
2 parents a39074c + 4102c2a commit cf808d7

File tree

3 files changed

+91
-26
lines changed

3 files changed

+91
-26
lines changed

bin/slack_alerts.py

Lines changed: 40 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77

88
import pandas as pd
99
import requests
10-
from loguru import logger
1110

1211

1312
def 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+
90104
def 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

133147
def main() -> None:

modules/output_primer_tsv.nf

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
process CREATE_PRIMER_TSV {
2+
3+
errorStrategy { task.attempt < 3 ? 'retry' : 'ignore' }
4+
maxRetries 2
5+
6+
input:
7+
path primer_seq_fasta
8+
9+
output:
10+
path "*.tsv", emit: primer_pair
11+
12+
script:
13+
def uuid = UUID.randomUUID().toString()
14+
"""
15+
amplicon_name=\$(basename "$primer_seq_fasta" | sed 's/\\.[^.]*\$//')
16+
17+
fwd=\$(awk 'NR==2' "$primer_seq_fasta")
18+
rev=\$(awk 'NR==4' "$primer_seq_fasta")
19+
20+
echo -e "\$amplicon_name\t\$fwd\t\$rev" > "${uuid}.tsv"
21+
"""
22+
}
23+
24+
process COLLECT_PRIMER_TSV {
25+
publishDir params.primer_handling, mode: 'copy', overwrite: true
26+
27+
errorStrategy { task.attempt < 3 ? 'retry' : 'ignore' }
28+
maxRetries 2
29+
30+
input:
31+
path all_primers
32+
33+
output:
34+
path "primer_pairs.tsv", emit: primer_pairs
35+
36+
script:
37+
"""
38+
awk -F"\t" 'BEGIN {print "amplicon_name\tfwd_sequence\treverse_sequence"} \$1 != "amplicon_name" {print \$0}' *.tsv > primer_pairs.tsv
39+
40+
"""
41+
}

subworkflows/primer_handling.nf

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ include {
1717
include { FILTER_WITH_CHOPPER } from "../modules/chopper"
1818
include { RASUSA_READ_DOWNSAMPLING } from "../modules/rasusa"
1919
include { WRITE_PRIMER_FASTA } from "../modules/write_primer_fasta"
20+
include { CREATE_PRIMER_TSV } from "../modules/output_primer_tsv"
21+
include { COLLECT_PRIMER_TSV } from "../modules/output_primer_tsv"
2022

2123

2224
workflow PRIMER_HANDLING {
@@ -59,6 +61,14 @@ workflow PRIMER_HANDLING {
5961
ch_refseq,
6062
)
6163

64+
CREATE_PRIMER_TSV(
65+
GET_PRIMER_SEQS.out
66+
)
67+
68+
COLLECT_PRIMER_TSV(
69+
CREATE_PRIMER_TSV.out.primer_pair.collect()
70+
)
71+
6272
GET_PRIMER_PATTERNS(
6373
GET_PRIMER_SEQS.out
6474
)

0 commit comments

Comments
 (0)