2a58331d892e73c2c4969455f750a7b0a41f3ba1
kate
  Thu Apr 4 16:43:45 2019 -0700
Extend right label feature to show count of cell types contributing to cluster, and total experiments for the factor (configurable). refs #21139

diff --git src/hg/hgTracks/factorSource.c src/hg/hgTracks/factorSource.c
index 0e955dc..223277a 100644
--- src/hg/hgTracks/factorSource.c
+++ src/hg/hgTracks/factorSource.c
@@ -13,63 +13,76 @@
 #include "hgTracks.h"
 #include "expRecord.h"
 #include "dystring.h"
 #include "factorSource.h"
 #include "bed6FloatScore.h"
 
 static struct factorSource *loadOne(char **row)
 /* Load up factorSource from array of strings. */
 {
 return factorSourceLoad(row);
 }
 
 /* Save info about factors and their motifs */
 struct factorSourceInfo {
     boolean showCellAbbrevs;
-    boolean showCellCounts;
+    boolean showExpCounts;
+    struct hash *factorExpCounts;
     struct hash *factorChoices;
     struct hash *motifTargets;
     struct bed6FloatScore *motifs;
 };
 
 boolean factorFilter(struct track *track, void *item)
 /* Returns true if an item should be passed by the filter. NOTE: single filter supported here*/
 {
 struct hash *factorHash = ((struct factorSourceInfo *)track->extraUiData)->factorChoices;
 if (track->extraUiData != NULL && factorHash != NULL)
     if (hashLookup(factorHash, ((struct factorSource *)item)->name) != NULL)
         return TRUE;
 return FALSE;
 }
 
 static void factorSourceLoadItems(struct track *track)
 /* Load all items (and motifs if table is present) in window */
 {
 bedLoadItem(track, track->table, (ItemLoader)loadOne);
 if (track->items == NULL)
     return;
 
 struct factorSourceInfo *fsInfo = NULL;
 AllocVar(fsInfo);
 track->extraUiData = fsInfo;
 
 // Check UI setting to show cell abbreviations or counts
 char varName[64];
 safef(varName, sizeof(varName), "%s.showCellAbbrevs", track->track);
 fsInfo->showCellAbbrevs = cartUsualBoolean(cart, varName, TRUE);
-safef(varName, sizeof(varName), "%s.showCellCounts", track->track);
-fsInfo->showCellCounts = cartUsualBoolean(cart, varName, TRUE);
+safef(varName, sizeof(varName), "%s.showExpCounts", track->track);
+fsInfo->showExpCounts = cartUsualBoolean(cart, varName, FALSE);
+
+if (fsInfo->showExpCounts)
+    {
+    struct sqlConnection *conn = hAllocConn(database);
+    char query[256];
+    char *inputTrackTable = trackDbRequiredSetting(track->tdb, "inputTrackTable");
+    sqlSafef(query, sizeof(query), 
+                "select factor, count(*) as num from %s group by factor", inputTrackTable);
+    fsInfo->factorExpCounts = sqlQuickHash(conn, query);
+    // TODO: If we're worried about performance, can remake as hash of ints
+    hFreeConn(&conn);
+    }
 
 // Filter factors based on multi-select
 filterBy_t *filter = filterBySetGet(track->tdb, cart, NULL);
 if (filter != NULL && filter->slChoices != NULL && differentString(filter->slChoices->name, "All"))
     {
     struct slName *choice;
     struct hash *factorHash = newHash(0);
     for (choice = filter->slChoices; choice != NULL; choice = choice->next)
         {
         hashAdd(factorHash, choice->name, NULL);
         }
     fsInfo->factorChoices = factorHash;
     filterItems(track, factorFilter, "include");
 }
 
@@ -117,71 +130,109 @@
         struct hash *targetHash = newHash(0);
         while ((row = sqlNextRow(sr)) != NULL)
             {
             char *target = row[0];
             char *motifs = row[1];   // string, comma-sep list, or empty string
             if (motifs[0] != 0)
                 hashAdd(targetHash, target, slNameListFromString(motifs, ','));
             }
         sqlFreeResult(&sr);
         fsInfo->motifTargets = targetHash;
         }
     }
 hFreeConn(&conn);
 }
 
-static int rightPixels(struct track *track, void *item)
+static int factorSourceRightPixels(struct track *track, void *item)
 /* Return number of pixels we need to the right. */
 {
+struct factorSourceInfo *fsInfo = (struct factorSourceInfo *)track->extraUiData;
+
+// can we test this here ?
+#ifdef BETTER_LAYOUT
+if (!(vis == tvFull || vis == tvPack))
+    return 0;
+#endif
 struct factorSource *fs = item;
 struct dyString *dy = dyStringNew(0);
+if (fsInfo->showExpCounts)
+    {
+    dyStringPrintf(dy, "%d", fs->expCount);
+    char *s = hashFindVal(fsInfo->factorExpCounts, fs->name);
+    int allCount = s ? sqlUnsigned(s) : 0;
+    if (fs->expCount != allCount)
+        dyStringPrintf(dy, "/%d", allCount);
+    if (fsInfo->showCellAbbrevs)
+        dyStringAppend(dy, " ");
+    }
+
+if (fsInfo->showCellAbbrevs)
+    {
     int i;
     for (i=0; i<fs->expCount; ++i)
         {
         int expNum = fs->expNums[i];
         char *label = track->sources[expNum]->name;
         dyStringAppend(dy, label);
         }
+    }
 int result = mgFontStringWidth(tl.font, dy->string);
 dyStringFree(&dy);
 return result;
 }
 
 static void factorSourceDrawItemAt(struct track *track, void *item, 
 	struct hvGfx *hvg, int xOff, int y, 
 	double scale, MgFont *font, Color color, enum trackVisibility vis)
 /* Draw factorSource item at a particular position. */
 {
 /* Figure out maximum score and draw box based on it. */
 struct factorSource *fs = item;
 int grayIx = grayInRange(fs->score, 0, 1000);
 color = shadesOfGray[grayIx];
 
 /* Calculate position, and draw box around where we are. */
 int heightPer = track->heightPer;
 int x1 = round((double)((int)fs->chromStart-winStart)*scale) + xOff;
 int x2 = round((double)((int)fs->chromEnd-winStart)*scale) + xOff;
 int w = x2-x1;
 if (w < 1)
     w = 1;
 hvGfxBox(hvg, x1, y, w, heightPer, color);
+if (vis != tvFull && vis != tvPack)
+    return;
 
 /* Draw text to the right */
+int x = x2 + tl.mWidth/2;
 struct factorSourceInfo *fsInfo = (struct factorSourceInfo *)track->extraUiData;
-if ((vis == tvFull || vis == tvPack) && fsInfo->showCellAbbrevs)
+if (fsInfo->showExpCounts)
+    {
+    struct dyString *ds = dyStringNew(0);
+    dyStringPrintf(ds, "%d", fs->expCount);
+    char *s = hashFindVal(fsInfo->factorExpCounts, fs->name);
+    int allCount = s ? sqlUnsigned(s) : 0;
+    if (fs->expCount != allCount)
+        dyStringPrintf(ds, "/%d", allCount);
+    if (fsInfo->showCellAbbrevs)
+        dyStringAppend(ds, " ");
+    char *label = dyStringCannibalize(&ds);
+    int w = mgFontStringWidth(font, label);
+    hvGfxTextCentered(hvg, x, y, w, heightPer, MG_BLACK, font, label);
+    x += w;
+    }
+if (fsInfo->showCellAbbrevs)
     {
-    int x = x2 + tl.mWidth/2;
     int i;
     for (i=0; i<fs->expCount; ++i)
         {
         int id = fs->expNums[i];
         char *label = track->sources[id]->name;
         int w = mgFontStringWidth(font, label);
         float score = fs->expScores[i];
         int grayIx = grayInRange(score, 0, 1000);
         int color = shadesOfGray[grayIx];
         hvGfxTextCentered(hvg, x, y, w, heightPer, color, font, label);
         x += w;
         }
     }
 }
 
@@ -283,37 +334,39 @@
 withLeftLabels = FALSE;
 genericDrawItems(track, seqStart, seqEnd, hvg, xOff, yOff, width,
 	font, color, vis);
 withLeftLabels = saveWithLeftLabels;
 }
 
 void factorSourceMethods(struct track *track)
 /* Set up special methods for factorSource type tracks. */
 {
 /* Start out as a bed, and then specialize loader, mark self as packable. */
 track->bedSize = 5;
 bedMethods(track);
 track->drawItemAt = factorSourceDrawItemAt;
 track->drawItems = factorSourceDraw;
 track->loadItems = factorSourceLoadItems;
-track->itemRightPixels = rightPixels;
+track->itemRightPixels = factorSourceRightPixels;
 
 /* Get the associated data describing the various sources. */
 track->expTable = trackDbRequiredSetting(track->tdb, SOURCE_TABLE);
 struct sqlConnection *conn = hAllocConn(database);
 char query[256];
 track->sourceCount = sqlTableSizeIfExists(conn, track->expTable);
 sqlSafef(query, sizeof(query), "select * from %s order by id", track->expTable);
 struct expRecord *exp, *expList = expRecordLoadByQuery(conn, query);
 int expIx;
 AllocArray(track->sources, track->sourceCount);
 for (exp=expList, expIx=0; exp != NULL; exp = exp->next, expIx += 1)
     track->sources[expIx] = exp;
 hFreeConn(&conn);
 
 /* Figure out how many pixels need to the right. */
+#ifdef UNUSED
 int rightCount = tl.mWidth/2;
 for (expIx=0; expIx < track->sourceCount; ++expIx)
     rightCount += mgFontStringWidth(tl.font, track->sources[expIx]->name);
 track->sourceRightPixels = rightCount;
+#endif
 }