7537f40b289d8ff6b05f41f66e683d97a130b794
kate
  Tue Nov 17 12:24:52 2020 -0800
Rough cut adding category file setting to barChart. refs #22075

diff --git src/hg/lib/barChartUi.c src/hg/lib/barChartUi.c
index 255b10b..3855f22 100644
--- src/hg/lib/barChartUi.c
+++ src/hg/lib/barChartUi.c
@@ -1,22 +1,23 @@
 /* Bar chart track controls */
 
 /* Copyright (C) 2015 The Regents of the University of California 
  * See README in this or parent directory for licensing information. */
 
 #include "cheapcgi.h"
 #include "cart.h"
+#include "net.h"
 #include "hui.h"
 #include "trackDb.h"
 #include "jsHelper.h"
 #include "hCommon.h"
 #include "rainbow.h"
 #include "htmlColor.h"
 #include "barChartCategory.h"
 #include "barChartUi.h"
 
 /* Restrict features on right-click (popup) version */
 static boolean isPopup = FALSE;
 
 /* Convenience functions for category filter controls */
 
 
@@ -144,93 +145,169 @@
 {
 char cartVar[1024];
 char buf[512];
 safef(cartVar, sizeof(cartVar), "%s.%s", track, BAR_CHART_LOG_TRANSFORM);
 boolean isLogTransform = cartCgiUsualBoolean(cart, cartVar, BAR_CHART_LOG_TRANSFORM_DEFAULT);
 safef(buf, sizeof buf, "%sViewLimitsMaxLabel %s", track, isLogTransform ? "disabled" : "");
 printf("<span class='%s'><b>View limits maximum:</b></span>\n", buf);
 safef(cartVar, sizeof(cartVar), "%s.%s", track, BAR_CHART_MAX_VIEW_LIMIT);
 int viewMax = cartCgiUsualInt(cart, cartVar, BAR_CHART_MAX_VIEW_LIMIT_DEFAULT);
 cgiMakeIntVarWithExtra(cartVar, viewMax, 4, isLogTransform ? "disabled" : "");
 char *unit = trackDbSettingClosestToHomeOrDefault(tdb, BAR_CHART_UNIT, "");
 printf("<span class='%s'> %s (range 0-%d)</span>\n", buf, unit, 
                                 round(barChartUiMaxMedianScore(tdb)));
 }
 
+// TODO: libify
+static boolean isUrl(char *url)
+{
+return ((startsWith("http://", url)
+   || startsWith("https://", url)
+   || startsWith("ftp://", url)));
+}
+
 struct barChartCategory *barChartUiGetCategories(char *database, struct trackDb *tdb)
-/* Get category colors and descriptions.  Use barChartColors setting if present.
-   If not, if there is a barChartBars setting, assign rainbow colors.
- * O/w look for a table naed track+Category, and use labels and colors there 
+/* Get category colors and descriptions.  
+   Use labels in tab-sep file specified by barChartCategoryUrl setting, o/w in barChartBars setting.
+   If colors are not specified via barChartColors setting or second column in category file,
+   assign rainbow colors.
  */
 {
-struct barChartCategory *categs = NULL;
+int count = 0;
+struct slName *labels = NULL, *colors = NULL;
+char *fileOrUrl = trackDbSetting(tdb, BAR_CHART_CATEGORY_URL);
+if (isEmpty(fileOrUrl))
+    {
+    // use trackDb settings for labels and perhaps colors
+    char *labelSetting = trackDbSetting(tdb, BAR_CHART_CATEGORY_LABELS);
+    if (isEmpty(labelSetting))
+        {
+        errAbort("barChart track %s missing required setting: %s or %s\n",
+                        tdb->track, BAR_CHART_CATEGORY_LABELS, BAR_CHART_CATEGORY_URL);
+        }
     char *words[BAR_CHART_MAX_CATEGORIES];
-char *colorWords[BAR_CHART_MAX_CATEGORIES];
-char *labels = trackDbSettingClosestToHome(tdb, BAR_CHART_CATEGORY_LABELS);
-char *colors = trackDbSettingClosestToHome(tdb, BAR_CHART_CATEGORY_COLORS);
-struct barChartCategory *categ = NULL;
+    count = chopLine(cloneString(labelSetting), words);
+    labels = slNameListFromStringArray(words, count);
+    char *colorSetting = trackDbSetting(tdb, BAR_CHART_CATEGORY_COLORS);
+    if (isNotEmpty(colorSetting))
+        {
+        int ct = chopLine(cloneString(colorSetting), words);
+        if (ct != count)
+            errAbort("barChart track %s settings mismatch: %s (%d) and  %s (%d)\n",
+                        tdb->track, BAR_CHART_CATEGORY_LABELS, count, BAR_CHART_CATEGORY_COLORS, ct);
+        colors = slNameListFromStringArray(words, count);
+        }
+    }
+else
+    {
+    // use file for labels and perhaps colors
+    struct lineFile *lf = NULL;
+
+#ifdef TODO
+// TODO: protect against network error
+struct errCatch *errCatch = errCatchNew();
+if (errCatchStart(errCatch))
+    {
+    if (isUrl(fileOrUrl))
+        {
+        lf = netLineFileOpen(fileOrUrl);
+    else
+        lf = lineFileMayOpen(fileOrUrl, TRUE);
+        }
+    }
+errCatchEnd(errCatch); 
+if (errCatch->gotError)
+    {
+    if (isNotEmpty(errCatch->message->string))
+        warn("unable to open %s: %s", fileOrUrl, errCatch->message->string);
+    }
+errCatchFree(&errCatch);
+#endif
 
-if (labels == NULL)
+    if (isUrl(fileOrUrl))
+        lf = netLineFileOpen(fileOrUrl);
+    else
+        lf = lineFileMayOpen(fileOrUrl, TRUE);
+    char *line = NULL;
+    int cols = 0;
+    while (lineFileNextReal(lf, &line))
+        {
+        char *words[2];
+        int wordCount = chopTabs(line, words);
+        if (cols)
             {
-    errAbort("barChart track %s missing required %s setting\n", tdb->track, BAR_CHART_CATEGORY_LABELS);
+            if (wordCount != cols)
+                errAbort("barChart track %s category file %s expecting %d words, got %d",
+                            tdb->track, fileOrUrl, cols, wordCount);
             }
         else
             {
-    int count = chopLine(cloneString(labels), words);
-    struct rgbColor *rainbow = getRainbow(&saturatedRainbowAtPos, count);
-    if (colors != NULL)
+            cols = wordCount;
+            }
+        slAddHead(&labels, slNameNew(words[0]));
+        if (wordCount == 2)
+            slAddHead(&colors, slNameNew(words[1]));
+        count++;
+        }
+    slReverse(&labels);
+    slReverse(&colors);
+    }
+
+// populate categories
+struct barChartCategory *categs = NULL, *categ = NULL;
+struct rgbColor *rainbow = NULL;
+if (!colors)
     {
-        int colorCount = chopLine(cloneString(colors), colorWords);
-        if (colorCount != count)
-            warn("barChart track %s mismatch between label (%d)  and color (%d) settings", 
-                    tdb->track, count, colorCount);
+    rainbow = getRainbow(&saturatedRainbowAtPos, count);
     }
 int i;
 char buf[6];
-    for (i=0; i<count; i++)
+for (i=0 ; i<count && labels; i++)
     {
     AllocVar(categ);
     categ->id = i;
     safef(buf, sizeof buf, "%d", i);
     categ->name = cloneString(buf);
-        categ->label = words[i];
+    categ->label = labels->name;
     if (colors)
         {
         unsigned rgb;
-            char *color = colorWords[i];
+        char *color = colors->name;
         if (htmlColorForCode(color, &rgb))
             {
             categ->color = rgb;
             }
         else if (htmlColorForName(color, &rgb))
             {
             categ->color = rgb;
             }
         else
             warn("barChart track %s unknown color %s. Must be one of %s\n",
                     tdb->track, color, slNameListToString(htmlColorNames(),','));
         }
     else
         {
         categ->color = ((rainbow[i].r & 0xff)<<16) + 
                     ((rainbow[i].g & 0xff)<<8) + 
                     ((rainbow[i].b & 0xff));
         }
     slAddHead(&categs, categ);
+    labels = labels->next;
+    if (colors)
+        colors = colors->next;
     }
 slReverse(&categs);
-    }
 return categs;
 }
 
 struct barChartCategory *barChartUiGetCategoryById(int id, char *database, 
                                                         struct trackDb *tdb)
 /* Get category info by id */
 {
 struct barChartCategory *categ;
 struct barChartCategory *categs = barChartUiGetCategories(database, tdb);
 for (categ = categs; categ != NULL; categ = categ->next)
     if (categ->id == id)
         return categ;
 return NULL;
 }