b68199a7676fbce5ede8ff948128981183dd1cd4
lrnassar
  Mon Mar 9 17:37:03 2026 -0700
Expanding the script to now also update the new hubPublic field for the hub's email (if possible). Now, when a hub fails, the contact email is printed on hgTracks. Also fixed a small logic bug in the 'if not' loop. Refs #33571

diff --git src/utils/qa/hubPublicAutoUpdate src/utils/qa/hubPublicAutoUpdate
index cfb6f7f5831..6781e56f88b 100755
--- src/utils/qa/hubPublicAutoUpdate
+++ src/utils/qa/hubPublicAutoUpdate
@@ -1,21 +1,21 @@
 #!/usr/bin/env python3
 #Lou Aug 2021
 """
 Given a server (dev/hgwbeta/rr) will query hubPublic table URLs and verify that the shortLabel,
-longLabel, dbCount and dbList matches what is in the hubPublic table. Any inconsistencies will be
-automatically updated. Will print out the commands as well as run them.
+longLabel, dbCount, dbList, and email matches what is in the hubPublic table. Any inconsistencies
+will be automatically updated. Will print out the commands as well as run them.
 
 Can optionally be run in test mode where it will only print commands without executing.
 
 Example run:
     hubPublicAutoUpdate dev
     hubPublicAutoUpdate hgwbeta -t
     hubPublicAutoUpdate rr
     
 """
 
 import subprocess,sys,argparse
 
 def parseArgs():
     """
     Parse the command line arguments.
@@ -58,34 +58,34 @@
     elif serverToQuery == 'rr':
         hgsqlInput = ['-h genome-centdb ','hgcentral']
     else:
         sys.exit("Server called must be either 'dev', 'hgwbeta', or 'rr'")
     return(hgsqlInput)
 
 def buildPubHubDic(hgsqlInput):
     """Build a dictionary out of the requested server's hubPublic table"""
     hubPublicDic = {}
     hubPublic = subprocess.run("/cluster/bin/x86_64/hgsql "+hgsqlInput[0]+"-e 'select * from hubPublic' "+hgsqlInput[1],\
                          check=True, shell=True, stdout=subprocess.PIPE, universal_newlines=True)
     hubPublicOutput = hubPublic.stdout.split('\n')[1:-1]
 
     for hub in hubPublicOutput:
         hub = hub.split('\t')
-        hubPublicDic[hub[0]] = {}
         hubPublicDic[hub[0]] = {'hubUrl':hub[0],'shortLabel':hub[1],'longLabel':hub[2],\
                             'registrationTime':hub[3],'dbCount':hub[4],\
-                            'dbList':hub[5],'descriptionUrl':hub[6]}
+                            'dbList':hub[5],'descriptionUrl':hub[6],
+                            'email':hub[7] if len(hub) > 7 else ''}
     return(hubPublicDic)
 
 def escapeDoubleQuotesOnLabels(label):
     """Look for single and double quotes in labels"""
     if '"' in label:
         label = label.replace('"','\\"')
     if "'" in label:
         label = label.replace("'","\\'")
     return(label)
 
 def curl(url):
     """Run curl command on URL - for http + ftp support"""
     rawCurlOutput = subprocess.run("curl --user-agent \"genome.ucsc.edu/net.c\" -skL --fail --connect-timeout 10 "+url,\
                          check=True, shell=True, stdout=subprocess.PIPE, universal_newlines=True)
     curlStdout = rawCurlOutput.stdout
@@ -93,31 +93,31 @@
 
 def buildCurrentHubTxtDic(hub):
     """Query hub.txt file and build dic of values"""
     currentHub = {}
     response = curl(hub)
     for line in response.splitlines():
         # Stop parsing at genome or track lines to avoid overwriting hub labels
         if line.startswith("genome ") or line.startswith("track "):
             break
         if "\t" in line.rstrip():
             line = line.split("\t")
             currentHub[line[0]] = line[1]
         else:
             line = line.split(" ")
             currentHub[line[0]] = " ".join(line[1:])
-    if not currentHub['descriptionUrl'].startswith("http") or currentHub['descriptionUrl'].startswith("ftp"):
+    if not (currentHub['descriptionUrl'].startswith("http") or currentHub['descriptionUrl'].startswith("ftp")):
         currentHub['descriptionUrl'] = '/'.join(hub.split('/')[0:len(hub.split('/'))-1])+"/"+currentHub['descriptionUrl']
     return(currentHub)
                     
 def queryHubTxt(currentHub,hub):
     """Query genomes.txt file and fill out dbList and dbCount values"""
     
     currentHub['dbList'] = []
     # A check for useOneFile to parse genome info from hub.txt instead of genomes.txt
     if currentHub.get('useOneFile') == 'on':
         genomeInfo = curl(hub)
     else:
         # Gets genome info from genomes.txt
         genomeFileLocation = currentHub['genomesFile'].rstrip().lstrip()
         if genomeFileLocation.startswith("http"):
             genomeUrl = genomeFileLocation
@@ -155,30 +155,34 @@
     if hubPublicDic[hub]['shortLabel'] != currentHub['shortLabel'].rstrip().lstrip():
         printHgsql(hub,'shortLabel',escapeDoubleQuotesOnLabels(currentHub['shortLabel'].rstrip().lstrip()),hgsqlInput,testMode)
         
     if hubPublicDic[hub]['longLabel'] != currentHub['longLabel'].rstrip().lstrip():     
         printHgsql(hub,'longLabel',escapeDoubleQuotesOnLabels(currentHub['longLabel'].rstrip().lstrip()),hgsqlInput,testMode)
 
     if int(hubPublicDic[hub]['dbCount']) != int(currentHub['dbCount']):
         printHgsql(hub,'dbCount',currentHub['dbCount'],hgsqlInput,testMode)
         
     if set(hubPublicDic[hub]['dbList'][:-1].split(',')) != set(currentHub['dbList']):
         printHgsql(hub,'dbList',",".join(currentHub['dbList'])+",",hgsqlInput,testMode)
 
     if hubPublicDic[hub]['descriptionUrl'] != currentHub['descriptionUrl'].rstrip().lstrip():
         printHgsql(hub,'descriptionUrl',currentHub['descriptionUrl'].rstrip().lstrip(),hgsqlInput,testMode)
 
+    if 'email' in currentHub and currentHub['email'].strip():
+        if hubPublicDic[hub]['email'] != currentHub['email'].strip():
+            printHgsql(hub,'email',currentHub['email'].strip(),hgsqlInput,testMode)
+    
 def hubPublicCompare(hubPublicDic,hgsqlInput,testMode,silent):
     """Query hub.txt files and compare values to hubPublic values"""
     for hub in hubPublicDic.keys():
         try: #Try for timeout connections
             currentHub = buildCurrentHubTxtDic(hub)
             currentHub = queryHubTxt(currentHub,hub)
             compareResults(hubPublicDic,currentHub,hub,hgsqlInput,testMode)
         except:
             if not silent:
                 print("The following hub has an error or is not responsive: "+str(hub))
             
 def main():
     """Initialize options and call other functions"""
     options = parseArgs()
     serverToQuery = options.server