9007158bac3746b1eb7f76513c8e8631e1ca1ca2
braney
  Fri Mar 21 13:18:22 2025 -0700
add decipher SQL tracks to quickLift support

diff --git src/hg/hgTracks/simpleTracks.c src/hg/hgTracks/simpleTracks.c
index 66c014c0027..79b4b07e6df 100644
--- src/hg/hgTracks/simpleTracks.c
+++ src/hg/hgTracks/simpleTracks.c
@@ -5995,54 +5995,81 @@
 }
 
 #endif /* GBROWSE */
 
 
 // The following few functions are shared by GAD, OMIM, DECIPHER, Superfamily.
 // Those tracks need an extra label derived from item name -- the extra label
 // is used as mouseover text for each item, and appears to the immediate left
 // of the feature in full mode.
 struct bedPlusLabel
 {
     struct bed bed; // inline, so struct bedPlusLabel * can be cast to struct bed *.
     char *label;
 };
 
-typedef char *labelFromNameFunction(char *name);
+typedef char *labelFromNameFunction(char *db, char *name);
 
 static void bedPlusLabelLoad(struct track *tg, labelFromNameFunction func)
 /* Load items from a bed table; if vis is pack or full, add extra label derived from item name. */
+{
+char *liftDb = cloneString(trackDbSetting(tg->tdb, "quickLiftDb"));
+
+if (liftDb == NULL)
     {
     struct bedPlusLabel *itemList = NULL;
     struct sqlConnection *conn = hAllocConn(database);
     int rowOffset = 0;
     struct sqlResult *sr = hRangeQuery(conn, tg->table, chromName, winStart, winEnd, NULL, &rowOffset);
     char **row = NULL;
     while ((row = sqlNextRow(sr)) != NULL)
         {
         struct bed *bed = bedLoad(row+rowOffset);
         struct bedPlusLabel *item = needMoreMem(bed, sizeof(struct bed), sizeof(struct bedPlusLabel));
         if (tg->visibility == tvPack || tg->visibility == tvFull)
-	item->label = cloneString(func(item->bed.name));
+            item->label = cloneString(func(database, item->bed.name));
         slAddHead(&itemList, item);
         }
     sqlFreeResult(&sr);
     hFreeConn(&conn);
     slReverse(&itemList);
     slSort(&itemList, bedCmp);
     tg->items = itemList;
     }
+else
+    {
+    /* if we're quicklifting get normal beds then add label */
+    loadSimpleBedWithLoader(tg, bedLoad);
+    struct bed *beds = tg->items;
+    struct bedPlusLabel *bedLabels = NULL;
+
+    struct bed *nextBed;
+    for(; beds; beds = nextBed)
+        {
+        nextBed = beds->next;
+
+        struct bedPlusLabel *bedLabel;
+        AllocVar(bedLabel);
+
+        bedLabel->bed = *beds;
+        bedLabel->label = cloneString(func(liftDb, bedLabel->bed.name));
+        slAddHead(&bedLabels, bedLabel);
+        }
+    slSort(&bedLabels, bedCmp);
+    tg->items = bedLabels;
+    }
+}
 
 void bedPlusLabelDrawAt(struct track *tg, void *item, struct hvGfx *hvg, int xOff, int y,
 			       double scale, MgFont *font, Color color, enum trackVisibility vis)
 /* Draw a single bed item at position.  If vis is full, draw the associated label to the left
  * of the item. */
 {
 struct bedPlusLabel *bpl = item;
 struct bed *bed = item;
 int heightPer = tg->heightPer;
 int s = max(bed->chromStart, winStart), e = min(bed->chromEnd, winEnd);
 if (s > e)
     return;
 int x1 = round((s-winStart)*scale) + xOff;
 int x2 = round((e-winStart)*scale) + xOff;
 int w = x2 - x1;
@@ -6064,39 +6091,39 @@
 
 static void bedPlusLabelMapItem(struct track *tg, struct hvGfx *hvg, void *item,
 				char *itemName, char *mapItemName, int start, int end,
 				int x, int y, int width, int height)
 /* Special mouseover text from item->label. (derived from genericMapItem) */
 {
 // Don't bother if we are imageV2 and a dense child.
 if(!theImgBox || tg->limitedVis != tvDense || !tdbIsCompositeChild(tg->tdb))
     {
     struct bedPlusLabel *bpl = item;;
     char *mouseOverText = isEmpty(bpl->label) ? bpl->bed.name : bpl->label;
     mapBoxHc(hvg, start, end, x, y, width, height, tg->track, mapItemName, mouseOverText);
     }
 }
 
-static char *collapseRowsFromQuery(char *query, char *sep, int limit)
+static char *collapseRowsFromQuery(char *db, char *query, char *sep, int limit)
 /* Return a string that is the concatenation of (up to limit) row[0]'s returned from query,
  * separated by sep.  Don't free the return value! */
 {
 static struct dyString *dy = NULL;
 if (dy == NULL)
     dy = dyStringNew(0);
 dyStringClear(dy);
-struct sqlConnection *conn = hAllocConn(database);
+struct sqlConnection *conn = hAllocConn(db);
 struct sqlResult *sr = sqlMustGetResult(conn, query);
 int i = 0;
 char **row = NULL;
 while ((row = sqlNextRow(sr)) != NULL)
     {
     eraseTrailingSpaces(row[0]);
     if (i != 0)
 	dyStringAppend(dy, sep);
     dyStringAppend(dy, row[0]);
     if (i == limit)
 	{
 	dyStringAppend(dy, " ...");
 	break;
 	}
     i++;
@@ -7182,138 +7209,140 @@
                 }
             else
                 {
                 proteinName = cloneString("");
 		}
 	    }
 	}
     }
 
 name = cloneString(proteinName);
 hFreeConn(&conn);
 
 return(name);
 }
 
-static char *superfamilyNameLong(char *name)
+static char *superfamilyNameLong(char *db, char *name)
 /* Return domain names of an entry of a Superfamily track item,
    each item may have multiple names
    due to possibility of multiple domains. */
 {
 char query[256];
 sqlSafef(query, sizeof(query), "select description from sfDescription where name='%s';", name);
-return collapseRowsFromQuery(query, "; ", 100);
+return collapseRowsFromQuery(db, query, "; ", 100);
 }
 
 static void superfamilyLoad(struct track *tg)
 /* Load superfamily items; in addition to items, store long description for mouseover/full mode. */
 {
 bedPlusLabelLoad(tg, superfamilyNameLong);
 }
 
 void superfamilyMethods(struct track *tg)
 /* Fill in methods for (simple) bed tracks. */
 {
 tg->loadItems   = superfamilyLoad;
 tg->drawItemAt  = bedPlusLabelDrawAt;
 tg->mapItem     = bedPlusLabelMapItem;
 tg->itemName    = superfamilyName;
 tg->mapItemName = superfamilyName;
 tg->nextPrevExon = simpleBedNextPrevEdge;
 }
 
-static char *gadDiseaseClassList(char *name)
+static char *gadDiseaseClassList(char *db, char *name)
 /* Return list of diseases associated with a GAD entry */
 {
 char query[256];
 sqlSafef(query, sizeof(query),
       "select distinct diseaseClassCode from gadAll "
       "where geneSymbol='%s' and association = 'Y' order by diseaseClassCode",
       name);
-return collapseRowsFromQuery(query, ",", 20);
+return collapseRowsFromQuery(db, query, ",", 20);
 }
 
-static char *gadDiseaseList(char *name)
+static char *gadDiseaseList(char *db, char *name)
 /* Return list of diseases associated with a GAD entry */
 {
 char query[256];
 sqlSafef(query, sizeof(query),
       "select distinct broadPhen from gadAll where geneSymbol='%s' and association = 'Y' "
       "order by broadPhen", name);
-return collapseRowsFromQuery(query, "; ", 20);
+return collapseRowsFromQuery(db, query, "; ", 20);
 }
 
 static void gadLoad(struct track *tg)
 /* Load GAD items as bed + label (used for mouseover; different label in draw routine!) */
 {
 bedPlusLabelLoad(tg, gadDiseaseList);
 }
 
 static void gadDrawAt(struct track *tg, void *item,
                       struct hvGfx *hvg, int xOff, int y,
                       double scale, MgFont *font, Color color, enum trackVisibility vis)
 /* Draw a single GAD item at position with extra label in full mode.
  * This is almost identical to bedPlusLabelDrawAt, but uses yet another function
  * to derive extra text in full mode. */
 {
 struct bed *bed = item;
 int heightPer = tg->heightPer;
 int x1 = round((double)((int)bed->chromStart-winStart)*scale) + xOff;
 int x2 = round((double)((int)bed->chromEnd-winStart)*scale) + xOff;
 int w;
 
 w = x2-x1;
 if (w < 1)
     w = 1;
 hvGfxBox(hvg, x1, y, w, heightPer, color);
 
 if (vis == tvFull)
     {
     // New text for label in full mode:
-    char *sDiseaseClasses = gadDiseaseClassList(bed->name);
+    char *liftDb = cloneString(trackDbSetting(tg->tdb, "quickLiftDb"));
+    char *db = (liftDb == NULL) ? database : liftDb;
+    char *sDiseaseClasses = gadDiseaseClassList(db, bed->name);
     int textWidth = mgFontStringWidth(font, sDiseaseClasses);
     hvGfxTextRight(hvg, x1-textWidth-2, y, textWidth, heightPer, MG_BLACK, font, sDiseaseClasses);
     }
 }
 
-static char *decipherCnvsPhenotypeList(char *name)
+static char *decipherCnvsPhenotypeList(char *db, char *name)
 /* Return list of diseases associated with a DECIPHER CNVs entry */
 {
 char query[256];
 static char list[4096];
 struct sqlConnection *conn = hAllocConn(database);
 if (sqlFieldIndex(conn, "decipherRaw", "phenotypes") >= 0)
     {
     list[0] = '\0';
     sqlSafef(query, sizeof(query),
         "select phenotypes from decipherRaw where id='%s'", name);
     struct sqlResult *sr = sqlMustGetResult(conn, query);
     char **row = sqlNextRow(sr);
     if ((row != NULL) && strlen(row[0]) >= 1)
         {
         char *prettyResult = replaceChars(row[0], "|", "; ");
         safecpy(list, sizeof(list), prettyResult);
         // freeMem(prettyResult);
         }
     sqlFreeResult(&sr);
     }
 else
     {
     sqlSafef(query, sizeof(query),
         "select distinct phenotype from decipherRaw where id='%s' order by phenotype", name);
     hFreeConn(&conn);
-    return collapseRowsFromQuery(query, "; ", 20);
+    return collapseRowsFromQuery(db, query, "; ", 20);
     }
 hFreeConn(&conn);
 return list;
 }
 
 void decipherCnvsLoad(struct track *tg)
 /* Load DECIPHER CNVs items with extra labels from decipherPhenotypeList. */
 {
 bedPlusLabelLoad(tg, decipherCnvsPhenotypeList);
 }
 
 Color decipherCnvsColor(struct track *tg, void *item, struct hvGfx *hvg)
 /* Return color to draw DECIPHER CNVs entry */
 {
 struct bed *bed = item;
@@ -7356,97 +7385,101 @@
                 {
                 col = MG_BLUE;
                 }
 	    else
 		{
                 col = MG_RED;
 		}
 	    }
 	sqlFreeResult(&sr);
 	}
     }
 hFreeConn(&conn);
 return(col);
 }
 
-static char *decipherSnvsPhenotypeList(char *name)
+static char *decipherSnvsPhenotypeList(char *db, char *name)
 /* Return list of diseases associated with a DECIPHER SNVs entry */
 {
 char query[256];
 static char list[4096];
-struct sqlConnection *conn = hAllocConn(database);
+struct sqlConnection *conn = hAllocConn(db);
 if (sqlFieldIndex(conn, "decipherSnvsRaw", "phenotypes") >= 0)
     {
     list[0] = '\0';
     sqlSafef(query, sizeof(query),
         "select phenotypes from decipherSnvsRaw where id='%s'", name);
     struct sqlResult *sr = sqlMustGetResult(conn, query);
     char **row = sqlNextRow(sr);
     if ((row != NULL) && strlen(row[0]) >= 1)
         {
         char *prettyResult = replaceChars(row[0], "|", "; ");
         safecpy(list, sizeof(list), prettyResult);
         // freeMem(prettyResult);
         }
     sqlFreeResult(&sr);
     }
 else
     {
     sqlSafef(query, sizeof(query),
         "select distinct phenotype from decipherSnvsRaw where id='%s' order by phenotype", name);
     hFreeConn(&conn);
-    return collapseRowsFromQuery(query, "; ", 20);
+    return collapseRowsFromQuery(db, query, "; ", 20);
     }
 hFreeConn(&conn);
 return list;
 }
 
 void decipherSnvsLoad(struct track *tg)
 /* Load DECIPHER SNVs items with extra labels from decipherSnvsPhenotypeList. */
 {
 bedPlusLabelLoad(tg, decipherSnvsPhenotypeList);
 }
 
 Color decipherSnvsColor(struct track *tg, void *item, struct hvGfx *hvg)
 /* Return color to draw DECIPHER SNV entry */
 {
 struct bed *bed = item;
 int col = tg->ixColor;
-struct sqlConnection *conn = hAllocConn(database);
+char *db = database;
+char *liftDb = cloneString(trackDbSetting(tg->tdb, "quickLiftDb"));
+if (liftDb != NULL)
+    db = liftDb;
+struct sqlConnection *conn = hAllocConn(db);
 struct sqlResult *sr;
 char **row;
 char query[256];
 char cond_str[256];
 char *decipherId = NULL;
 
 /* So far, we can just remove "chr" from UCSC chrom names to get DECIPHER names */
 char *decipherChrom = bed->chrom;
 if (startsWithNoCase("chr", bed->chrom))
     decipherChrom += 3;
 
 /* color scheme:
     BLACK:      If the entry is likely or definitely pathogenic
     DARK GRAY:  If the entry is uncertain or unknown
     LIGHT GRAY: If the entry is likely or definitely benign
 */
 
 sqlSafef(cond_str, sizeof(cond_str),"name='%s' ", bed->name);
-decipherId = sqlGetField(database, "decipherSnvs", "name", cond_str);
+decipherId = sqlGetField(db, "decipherSnvs", "name", cond_str);
 
 if (decipherId != NULL)
     {
-    if (hTableExists(database, "decipherSnvsRaw"))
+    if (hTableExists(db, "decipherSnvsRaw"))
         {
         sqlSafef(query, sizeof(query), "select pathogenicity from decipherSnvsRaw where "
             "id = '%s' and chr = '%s' and start = '%d' and end = '%d'",
             decipherId, decipherChrom, bed->chromStart+1, bed->chromEnd);
 	sr = sqlGetResult(conn, query);
         col = MG_GRAY;
         if ((row = sqlNextRow(sr)) != NULL)
             {
             char *ucPathogenicity = cloneString(row[0]);
             strUpper(ucPathogenicity);
 	    if (endsWith(ucPathogenicity, "PATHOGENIC"))
                 {
                 col = MG_BLACK;
                 }
 	    else if (endsWith(ucPathogenicity, "BENIGN"))
@@ -13290,31 +13323,31 @@
     else if (sameString(mode, "X-linked dominant"))
         dyStringAppend(dy, "XLD");
     else if (sameString(mode, "X-linked recessive"))
         dyStringAppend(dy, "XLR");
     else if (sameString(mode, "Y-linked"))
         dyStringAppend(dy, "YL");
     else if (!isEmpty(mode))
         dyStringAppend(dy, mode);
 
     if (modes->next)
         dyStringAppend(dy, "/");
     }
 return dy->string;
 }
 
-static char *omimGene2DisorderList(char *name)
+static char *omimGene2DisorderList(char *db, char *name)
 /* Return list of disorders associated with a OMIM entry.  Do not free result! */
 {
 static struct dyString *dy = NULL;
 struct sqlConnection *conn;
 char query[4096];
 
 if (dy == NULL)
     dy = dyStringNew(0);
 dyStringClear(dy);
 
 // get gene symbol(s) first
 conn = hAllocConn(database);
 sqlSafef(query,sizeof(query),
         "select geneSymbol from omimGeneMap2 where omimId =%s", name);
 char buf[4096];
@@ -13699,31 +13732,31 @@
         geneSymbol = sqlQuickString(conn, query);
         if (geneSymbol && differentString(geneSymbol, "0"))
             {
 	    // pick the first one, if multiple gene symbols exist
             chp = strstr(geneSymbol, ",");
 	    if (chp != NULL) *chp = '\0';
 	    dyStringAppend(name, geneSymbol);
 	    }
 	}
     }
 
 hFreeConn(&conn);
 return(name->string);
 }
 
-static char *cosmicTissueList(char *name)
+static char *cosmicTissueList(char *db, char *name)
 /* Return list of tumor tissues associated with a COSMIC entry.  Do not free result! */
 {
 static struct dyString *dy = NULL;
 struct sqlConnection *conn = hAllocConn(database);
 
 if (dy == NULL)
     dy = dyStringNew(0);
 dyStringClear(dy);
 
 char query[256];
 sqlSafef(query,sizeof(query),
         "select concat(gene_name,' ',mut_syntax_aa) from cosmicRaw where cosmic_mutation_id ='%s'", name);
 char buf[256];
 char *ret = sqlQuickQuery(conn, query, buf, sizeof(buf));
 
@@ -13762,31 +13795,31 @@
     if ((chp != NULL) && (strlen(chp) > 3))
         {
         chp++;
         chp++;
         chp++;
         chp++;
         *chp = '\0';
         }
     dyStringAppend(dy, " (");
     dyStringAppend(dy, ret);
     dyStringAppend(dy, "\%)");
     }
 
 sqlSafef(query,sizeof(query),
         "select tumour_site from cosmicRaw where cosmic_mutation_id ='%s' order by tumour_site", name);
-char *disorders = collapseRowsFromQuery(query, ",", 4);
+char *disorders = collapseRowsFromQuery(db, query, ",", 4);
 if (isNotEmpty(disorders))
     {
     dyStringAppend(dy, " ");
     dyStringAppend(dy, disorders);
     }
 hFreeConn(&conn);
 return(dy->string);
 }
 
 static void cosmicLoad(struct track *tg)
 /* Load COSMIC items, storing long label from cosmicTissueList */
 {
 bedPlusLabelLoad(tg, cosmicTissueList);
 }
 
@@ -13799,31 +13832,31 @@
 tg->nextPrevExon  = simpleBedNextPrevEdge;
 }
 
 void omimGene2Methods (struct track *tg)
 /* Methods for version 2 of OMIM Genes track. */
 {
 tg->loadItems	  = omimGene2Load;
 tg->itemColor     = omimGene2Color;
 tg->itemName	  = omimGene2Name;
 tg->itemNameColor = omimGene2Color;
 tg->drawItemAt    = bedPlusLabelDrawAt;
 tg->mapItem       = bedPlusLabelMapItem;
 tg->nextPrevExon = simpleBedNextPrevEdge;
 }
 
-static char *omimAvSnpAaReplacement(char *name)
+static char *omimAvSnpAaReplacement(char *db, char *name)
 /* Return replacement string associated with a OMIM AV (Allelic Variant) entry */
 {
 static char omimAvSnpBuffer[256];
 struct sqlConnection *conn;
 char query[256];
 struct sqlResult *sr;
 char **row;
 
 omimAvSnpBuffer[0] = '\0';
 
 conn = hAllocConn(database);
 sqlSafef(query,sizeof(query),
         "select repl2, dbSnpId, description from omimAv where avId='%s'", name);
 sr = sqlMustGetResult(conn, query);
 row = sqlNextRow(sr);
@@ -13842,31 +13875,31 @@
 /* Load OMIM AV items, storing long label from omimAvSnpAaReplacement. */
 {
 bedPlusLabelLoad(tg, omimAvSnpAaReplacement);
 }
 
 void omimAvSnpMethods (struct track *tg)
 /* Methods for OMIM AV (Allelic Variant) SNPs. */
 {
 tg->loadItems  = omimAvSnpLoad;
 tg->drawItemAt = bedPlusLabelDrawAt;
 tg->mapItem    = bedPlusLabelMapItem;
 tg->nextPrevExon = simpleBedNextPrevEdge;
 }
 
 
-static char *omimLocationDescription(char *name)
+static char *omimLocationDescription(char *db, char *name)
 /* Return description of an OMIM entry */
 {
 static char omimLocationBuffer[512];
 struct sqlConnection *conn;
 char query[256];
 
 omimLocationBuffer[0] = '\0';
 
 conn = hAllocConn(database);
 sqlSafef(query,sizeof(query),
         "select geneName from omimGeneMap2 where omimId=%s", name);
 (void)sqlQuickQuery(conn, query, omimLocationBuffer, sizeof(omimLocationBuffer));
 hFreeConn(&conn);
 return(omimLocationBuffer);
 }
@@ -14066,38 +14099,38 @@
 
 /* set the color to red if the entry is listed in morbidmap */
 sqlSafef(query, sizeof(query), "select geneSymbols from omimMorbidMap where omimId=%s", el->name);
 geneSymbols = sqlQuickString(conn, query);
 hFreeConn(&conn);
 if (geneSymbols != NULL)
     {
     return hvGfxFindColorIx(hvg, 255, 0, 0);
     }
 else
     {
     return hvGfxFindColorIx(hvg, 0, 0, 200);
     }
 }
 
-static char *omimGeneDiseaseList(char *name)
+static char *omimGeneDiseaseList(char *db, char *name)
 /* Return list of diseases associated with a OMIM entry */
 {
 char query[256];
 sqlSafef(query,sizeof(query),
       "select distinct description from omimMorbidMap, omimGene "
       "where name='%s' and name=cast(omimId as char) order by description", name);
-return collapseRowsFromQuery(query, "; ", 20);
+return collapseRowsFromQuery(db, query, "; ", 20);
 }
 
 static void omimGeneLoad(struct track *tg)
 /* Load OMIM Genes, storing long label from omimGeneDiseaseList. */
 {
 bedPlusLabelLoad(tg, omimGeneDiseaseList);
 }
 
 void omimGeneMethods (struct track *tg)
 /* Methods for original OMIM Genes track. */
 {
 tg->loadItems     = omimGeneLoad;
 tg->itemColor     = omimGeneColor;
 tg->itemNameColor = omimGeneColor;
 tg->itemName      = omimGeneName;