2dda14f9c5394e1d6fa30f3da69e74b8e8b84413
lrnassar
  Tue Jun 30 15:34:05 2026 -0700
Fix panelApp otto crash when reading Ensembl gene locations. The script hardcoded the Ensembl release sub-key (GRch37/82, GRch38/90) when pulling gene coordinates, but PanelApp Australia moved to releases 87/115. Every Australia gene then failed both assembly lookups, leaving the dataframes empty and raising a column-count ValueError. Added an ensemblBuild() helper that selects whichever release key the API returns, and routed all gene/STR coordinate and ensembl_id lookups through it. England (still on 82/90) is unaffected.

No RM, refs otto

diff --git src/hg/utils/otto/panelApp/doPanelApp.py src/hg/utils/otto/panelApp/doPanelApp.py
index 98794cade96..97d999c4613 100755
--- src/hg/utils/otto/panelApp/doPanelApp.py
+++ src/hg/utils/otto/panelApp/doPanelApp.py
@@ -107,30 +107,37 @@
     try:
         jData = requests_get_with_retry(url)
         
         # If jData is empty create, else append
         if "error" in jData.keys() or not "results" in jData.keys():
             raise Exception("Error in keys when downloading %s" % url)
         if "count" in jData and not "page" in url:
             print("API says that there are %d results for url %s" % (jData["count"], url))
         results.extend(jData["results"])
         if "next" in jData and jData["next"] is not None: # need to get next URL
             return getAllPages(jData["next"], results)
     except Exception as e:
         raise Exception("Error when downloading %s: %s" % (url, str(e)))
     return results
 
+def ensemblBuild(ensembl_genes, assembly):
+    """Return the newest Ensembl-release dict for an assembly. The release-number sub-key varies
+    by PanelApp instance (England GRch37/82, GRch38/90; Australia GRch37/87, GRch38/115)."""
+    releases = ensembl_genes[assembly]
+    relKey = sorted(releases.keys(), key=int)[-1]
+    return releases[relKey]
+
 def downloadCnvs(url):
     Error = True
     continuous_count=0
     res = getAllPages(url, results=[])
 
     num_gene_data = len(res)
     print("Got %d CNVs" % num_gene_data)
     count = 0
     continuous_count = 0
     hg19_dict = dict()
     hg38_dict = dict()
 
     for geneCount, cnvData in enumerate(res):
         temp_attribute_dictionary = dict()
         string_dict_key = 'gene_{}'.format(geneCount)
@@ -310,59 +317,59 @@
         chromosome = res[count]['chromosome']    
         chromosome = 'chr{}'.format(chromosome)
         confidence_level = res[count]['confidence_level']
         entity_name = res[count]['entity_name']
         entity_type = res[count]['entity_type']
         evidence = ' '.join(res[count]['evidence'])
         
         gene_data = res[count]['gene_data']
         if gene_data:
             alias = ' '.join(gene_data.get('alias', []))
             biotype = gene_data['biotype']
         else:
             alias = ""
             biotype = ""
         try:
-            ensembl_id_37 = gene_data['ensembl_genes']['GRch37']['82']['ensembl_id']
+            ensembl_id_37 = ensemblBuild(gene_data['ensembl_genes'], 'GRch37')['ensembl_id']
         except:
             ensembl_id_37 = "None"
         try:
-            ensembl_id_38 = gene_data['ensembl_genes']['GRch38']['90']['ensembl_id']
+            ensembl_id_38 = ensemblBuild(gene_data['ensembl_genes'], 'GRch38')['ensembl_id']
         except:
             ensembl_id_38 = "None"
         
         gene_name = gene_data['gene_name']
         #gene_symbol = gene_data['gene_symbol']
         #hgnc_date_symbol_changed = gene_data['hgnc_date_symbol_changed']
         hgnc_id = gene_data['hgnc_id']
         hgnc_symbol = gene_data['hgnc_symbol']
         if str(gene_data['omim_gene']) == "None":
             omim_gene = "None"
         else:
             omim_gene = ' '.join(gene_data['omim_gene'])
         grch37_coordinates = res[count]['grch37_coordinates']
         if grch37_coordinates == None:
-            coordinates = gene_data['ensembl_genes']['GRch37']['82']['location'] 
+            coordinates = ensemblBuild(gene_data['ensembl_genes'], 'GRch37')['location']
             location = coordinates.split(':')
             grch37_coordinates = location[1].split('-')
         chromStart_19 = int(grch37_coordinates[0])
         chromEnd_19 = int(grch37_coordinates[1])
 
         # hg38
         grch38_coordinates = res[count]['grch38_coordinates']
         if grch38_coordinates == None:
-            coordinates = gene_data['ensembl_genes']['GRch38']['90']['location'] 
+            coordinates = ensemblBuild(gene_data['ensembl_genes'], 'GRch38')['location']
             location = coordinates.split(':')
             grch38_coordinates = location[1].split('-')
 
         mode_of_inheritance = res[count]['mode_of_inheritance']
         normal_repeats = res[count]['normal_repeats']
         chromStart_38 = int(grch38_coordinates[0])
         chromEnd_38 = int(grch38_coordinates[1])
         
         panel = res[count]['panel']
         disease_group = panel['disease_group']
         disease_sub_group = panel['disease_sub_group']
         hash_id = panel['hash_id']
         idd = panel['id']
         panel_name = panel['name']
         relevant_disorders = ' '.join(panel['relevant_disorders'])
@@ -609,40 +616,40 @@
         
         #filter by onlyPanels early, if specified
         if onlyPanels is not None:
             res = [entry for entry in res if entry.get('panel', {}).get('id') in onlyPanels]
 
         num_gene_variant = len(res)
         count = 0
         while count != num_gene_variant:
             temp_attribute_dictionary = dict()
             string_dict_key = 'gene_{}'.format(continuous_count)
 
             gene_range_37 = None
             gene_range_38 = None
 
             try:
-                ensembl_genes_GRch37_82_location = res[count]['gene_data']['ensembl_genes']['GRch37']['82']['location']
+                ensembl_genes_GRch37_82_location = ensemblBuild(res[count]['gene_data']['ensembl_genes'], 'GRch37')['location']
                 location_37 = ensembl_genes_GRch37_82_location.split(':')
                 chromo_37 = 'chr'+location_37[0]
                 gene_range_37 = location_37[1].split('-')
                 # on hg19, we have added a chrMT sequence later.
             except:
                 genes_missing_info.append(res[count]['gene_data']['gene_symbol']+"/hg19")
 
             try:
-                ensembl_genes_GRch38_90_location = res[count]['gene_data']['ensembl_genes']['GRch38']['90']['location']
+                ensembl_genes_GRch38_90_location = ensemblBuild(res[count]['gene_data']['ensembl_genes'], 'GRch38')['location']
                 location_38 = ensembl_genes_GRch38_90_location.split(':')
                 chromo_38 = 'chr'+location_38[0]
                 # Change mitochondrial chromosomal suffix from MT -> M for hg38 only
                 if chromo_38 == "chrMT":
                     chromo_38 = "chrM"
 
                 gene_range_38 = location_38[1].split('-')
             except:
                 genes_missing_info.append(res[count]['gene_data']['gene_symbol']+"/hg38")
 
             if gene_range_37 is None and gene_range_38 is None:
                 #print("gene without location on any assembly: %s" % res[count])
                 genes_no_location.append(res[count]['gene_data'])
                 count+=1
                 continue
@@ -693,32 +700,32 @@
             try:
                 biotype = res[count]['gene_data']['biotype']
 
                 if biotype == 'protein_coding':
                     biotype = 'Protein Coding'
                 
                 temp_attribute_dictionary['biotype'] = biotype
                 if biotype == None:
                     temp_attribute_dictionary['biotype'] = ''
             except:
                 temp_attribute_dictionary['biotype'] = ''
 
             #-----------------------------------------------------------------------------------------------------------    
 
             try:
-                ensembl_genes_GRch37_82_ensembl_id = res[count]['gene_data']['ensembl_genes']['GRch37']['82']['ensembl_id']
-                ensembl_genes_GRch38_90_ensembl_id = res[count]['gene_data']['ensembl_genes']['GRch38']['90']['ensembl_id']
+                ensembl_genes_GRch37_82_ensembl_id = ensemblBuild(res[count]['gene_data']['ensembl_genes'], 'GRch37')['ensembl_id']
+                ensembl_genes_GRch38_90_ensembl_id = ensemblBuild(res[count]['gene_data']['ensembl_genes'], 'GRch38')['ensembl_id']
 
             except:
                 ensembl_genes_GRch37_82_ensembl_id = ''
 
             #-----------------------------------------------------------------------------------------------------------
 
             gene_type_list = ['confidence_level', 'phenotypes', 'mode_of_inheritance', 'tags']
 
             for attribute in gene_type_list:
                 try:
                     x = res[count][attribute]
                     if not x:
                         temp_attribute_dictionary[attribute] = ''
                     else:
                         pre = ' '.join(res[count][attribute])