855cad962a8fc6abc4a053c7a4575b263fa864d3 hiram Wed May 27 14:45:12 2026 -0700 correctly use the hg.conf settings and correct bounce email, can now monitor both hgwdev and the RR hgcentral table refs #31811 diff --git src/hg/utils/otto/userRequests/ottoRequest.py src/hg/utils/otto/userRequests/ottoRequest.py index 75141e61783..d1ec80e228b 100755 --- src/hg/utils/otto/userRequests/ottoRequest.py +++ src/hg/utils/otto/userRequests/ottoRequest.py @@ -100,36 +100,36 @@ elif c == 't': out.append('\t') elif c == '\\': out.append('\\') elif c == '0': out.append('\0') else: out.append(s[i:i+2]) i += 2 else: out.append(s[i]) i += 1 return ''.join(out) -def hgsqlQuery(db, sql): +def hgsqlQuery(host, db, sql): """Run a SQL query via hgsql and return rows as list of dicts. hgsql -B emits tabs/newlines/backslashes inside field values as literal \\t / \\n / \\\\ so each row stays on one line undo that on each field before returning.""" - cmd = ['/cluster/bin/x86_64/hgsql', db, '-N', '-B', '-e', sql] + cmd = ['/cluster/bin/x86_64/hgsql', '-h', host, db, '-N', '-B', '-e', sql] result = subprocess.run(cmd, capture_output=True, text=True) if result.returncode != 0: sys.exit(f"hgsql error: {result.stderr.strip()}") rows = [] if not result.stdout.strip(): return rows for line in result.stdout.strip().split('\n'): fields = [unescapeMysql(f) for f in line.split('\t')] rows.append({ 'id': fields[0], 'requestType': fields[1], 'fromDb': fields[2], 'toDb': fields[3], 'email': fields[4], 'comment': fields[5], @@ -168,79 +168,83 @@ m2 = re.match(r"; betterName: '([^']*)'(.*)$", rest, re.DOTALL) if m2: betterName = m2.group(1) rest = m2.group(2) m3 = re.match(r"; comment: '(.*)'\s*$", rest, re.DOTALL) if m3: userComment = m3.group(1) else: userComment = rest.lstrip('; ') return name, betterName, userComment -def sendMail(toAddr, subject, body, fromAddr=None, bccAddr=None): +def sendMail(toAddr, subject, body, fromAddr=None, bccAddr=None, bounceAddr=None): """Send email via /usr/sbin/sendmail. If fromAddr is provided it is used as the envelope sender (-f) and the From: header so that bounces return to that address. If bccAddr is provided, sendmail -t reads it from the header, delivers a copy, and strips the Bcc: line before transmission.""" headers = f"To: {toAddr}\nSubject: {subject}" if bccAddr: headers = f"Bcc: {bccAddr}\n{headers}" if fromAddr: headers = (f"From: {fromAddr}\n" f"Reply-To: {fromAddr}\n" f"Return-Path: {fromAddr}\n" f"{headers}") message = f"{headers}\n\n{body}\n" cmd = ['/usr/sbin/sendmail', '-t'] - if fromAddr: - cmd += ['-f', fromAddr] + if bounceAddr: + cmd += ['-f', bounceAddr] result = subprocess.run(cmd, input=message, capture_output=True, text=True) if result.returncode != 0: print(f"Warning: sendmail failed: {result.stderr.strip()}", file=sys.stderr) return False return True def main(): parser = argparse.ArgumentParser( description='Process pending ottoRequest entries.') parser.add_argument('-c', '--conf', default='/usr/local/apache/cgi-bin/hg.conf', help='Path to hg.conf [default: %(default)s]') args = parser.parse_args() # bind to a local so the FD stays open for the lifetime of main() _lock = acquireSingletonLock() # noqa: F841 conf = parseHgConf(args.conf) - dbName = conf.get('central.db') - if not dbName: - sys.exit("Error: central.db not defined in config") + dbHost = conf.get('db.host') + if not dbHost: + sys.exit("Error: db.host not defined in config") + dbUser = conf.get('db.user') + if not dbUser: + sys.exit("Error: db.user not defined in config") - table = conf.get('ottoTable', 'ottoRequest') + ottoDb = conf.get('ottoDb', 'hgcentraltest') + ottoTable = conf.get('ottoTable', 'ottoRequest') # find pending requests sql = (f"SELECT id, requestType, fromDb, toDb, email, comment, " - f"requestTime FROM {table} WHERE status = 0") - pending = hgsqlQuery(dbName, sql) + f"requestTime FROM {ottoTable} WHERE status = 0") + pending = hgsqlQuery(dbHost, ottoDb, sql) if not pending: return # nothing to do -- silent for cron for req in pending: reqType = req['requestType'] bccAddr = BCC_BY_TYPE.get(reqType) if not bccAddr: print(f"Warning: unknown requestType '{reqType}' for" f" request #{req['id']}, skipping", file=sys.stderr) continue userEmail = req.get('email', '') if not userEmail: @@ -270,27 +274,27 @@ else: body = ( f"Your alignment request has been received and is being\n" f"processed.\n" f"\n" f"Request details:\n" f" From: {req['fromDb']}\n" f" To: {req['toDb']}\n" f" Comment: {req['comment'].rstrip()}\n" f" Submitted: {req['requestTime']}\n" f"\n" f"Will advise when this alignment is available in the genome browser.\n" f"\n" f"-- UCSC Genome Browser\n" ) + bitParts = ["h", "cla", "wson", "@", "uc", "sc.", "ed", "u"] if sendMail(userEmail, subject, body, - fromAddr=NOTIFY_FROM, bccAddr=bccAddr): - hgsqlUpdate(dbName, - f"UPDATE {table} SET status = 1" + fromAddr=NOTIFY_FROM, bccAddr=bccAddr, bounceAddr="".join(bitParts)): + hgsqlUpdate(ottoDb, f"UPDATE {ottoTable} SET status = 1" f" WHERE id = {req['id']}") else: print(f"Failed to send notification for request #{req['id']}", file=sys.stderr) if __name__ == '__main__': main()