src/hg/hgTracks/searchTracks.c 1.7

1.7 2010/05/30 04:00:39 larrym
refactor code to allow variable number of metadata selects
Index: src/hg/hgTracks/searchTracks.c
===================================================================
RCS file: /projects/compbio/cvsroot/kent/src/hg/hgTracks/searchTracks.c,v
retrieving revision 1.6
retrieving revision 1.7
diff -b -B -U 4 -r1.6 -r1.7
--- src/hg/hgTracks/searchTracks.c	30 May 2010 01:38:17 -0000	1.6
+++ src/hg/hgTracks/searchTracks.c	30 May 2010 04:00:39 -0000	1.7
@@ -17,8 +17,10 @@
 
 static char const rcsid[] = "$Id$";
 
 #define ANYLABEL "Any"
+#define METADATA_NAME_PREFIX "hgt.metaDataName"
+#define METADATA_VALUE_PREFIX "hgt.metaDataValue"
 
 static int gCmpGroup(const void *va, const void *vb)
 /* Compare groups based on label. */
 {
@@ -53,9 +55,9 @@
 static boolean isDescriptionMatch(struct track *track, char *str, struct hash *trackMetadata)
 {
 // We parse str and look for every word ANYWHERE in track description (i.e. google style).
 // XXXX currently quite primitive; do stemming, strip html markup ??
-// Also, we should fold all metadata into the description of every track.
+// trackMetaData contains tracks already found via metadata searches.
 boolean found = FALSE;
 
 if(str && strlen(str))
     {
@@ -119,9 +121,9 @@
 slSort(&termList, slNameCmpCase);
 count++; // make room for "Any"
 retval = needMem(sizeof(char *) * count);
 retval[0] = cloneString(ANYLABEL);
-for(i=1; termList != NULL;termList = termList->next, i++)
+for(i = 1; termList != NULL;termList = termList->next, i++)
     {
     retval[i] = cloneString(termList->name);
     }
 *terms = retval;
@@ -191,17 +193,16 @@
 char *nameSearch = cartOptionalString(cart, "hgt.nameSearch");
 char *nameOp = cartOptionalString(cart, "hgt.nameOp");
 char *descSearch = cartOptionalString(cart, "hgt.descSearch");
 char *groupSearch = cartOptionalString(cart, "hgt.groupSearch");
-char *metaName = cartUsualString(cart, "hgt.metaName", "cell");
-char *metaOp = cartOptionalString(cart, "hgt.metaOp");
-char *metaSearch = cartOptionalString(cart, "hgt.metaSearch");
-char *antibodySearch = cartOptionalString(cart, "hgt.antibodySearch");
-char **terms;
+boolean doSearch = sameString(cartOptionalString(cart, searchTracks), "Search");
 struct sqlConnection *conn = hAllocConn(database);
 boolean metaDbExists = sqlTableExists(conn, "metaDb");
 struct slRef *tracks = NULL;
-int tracksFound = 0;
+int numMetaDataSelects, tracksFound = 0;
+int numMetaDataNonEmpty = 0;
+char **metaDataName;
+char **metaDataValue;
 struct hash *parents = newHash(4);
 
 getTrackList(&groupList, -2);
 slSort(&groupList, gCmpGroup);
@@ -216,14 +217,13 @@
             internalErr();
         }
     }
 
-// cartWebStart(cart, database, );
+hPrintf("<form action='%s' name='SearchTracks' method='post'>\n\n", hgTracksName());
+
 webStartWrapperDetailedNoArgs(cart, database, "", "Track Search (prototype!)", FALSE, FALSE, FALSE, FALSE);
 
 hPrintf("<input type='hidden' name='db' value='%s'>\n", database);
-
-hPrintf("<form action='%s' name='SearchTracks' method='post'>\n\n", hgTracksName());
 hPrintf("<table>\n");
 
 hPrintf("<tr><td></td><td><b>Description:</b></td><td>contains</td>\n");
 hPrintf("<td><input type='text' name='hgt.descSearch' value='%s' size='80'></td></tr>\n", descSearch == NULL ? "" : descSearch);
@@ -236,29 +236,77 @@
 hPrintf("<td><b>Group</b></td><td>is</td>\n<td>\n");
 cgiMakeDropListFull("hgt.groupSearch", labels, groups, numGroups, groupSearch, NULL);
 hPrintf("</td></tr>\n");
 
+// figure out how many metadata selects are visible.
+
+for(numMetaDataSelects = 0;;)
+    {
+    char buf[256];
+    safef(buf, sizeof(buf), "%s%d", METADATA_NAME_PREFIX, numMetaDataSelects + 1);
+    char *str = cartOptionalString(cart, buf);
+    if(isEmpty(str))
+        break;
+    else
+        numMetaDataSelects++;
+    }
+
+if(numMetaDataSelects)
+    {
+    metaDataName = needMem(sizeof(char *) * numMetaDataSelects);
+    metaDataValue = needMem(sizeof(char *) * numMetaDataSelects);
+    int i;
+    for(i = 0; i < numMetaDataSelects; i++)
+        {
+        char buf[256];
+        safef(buf, sizeof(buf), "%s%d", METADATA_NAME_PREFIX, i + 1);
+        metaDataName[i] = cartOptionalString(cart, buf);
+        safef(buf, sizeof(buf), "%s%d", METADATA_VALUE_PREFIX, i + 1);
+        metaDataValue[i] = cartOptionalString(cart, buf);
+        if(!strcmp(metaDataValue[i], ANYLABEL))
+            metaDataValue[i] = NULL;
+        if(!isEmpty(metaDataValue[i]))
+            numMetaDataNonEmpty++;
+        }
+    }
+else
+    {
+    // create defaults
+    numMetaDataSelects = 2;
+    metaDataName = needMem(sizeof(char *) * numMetaDataSelects);
+    metaDataValue = needMem(sizeof(char *) * numMetaDataSelects);
+    metaDataName[0] = "cell";
+    metaDataName[1] = "antibody";
+    metaDataValue[0] = ANYLABEL;
+    metaDataValue[1] = ANYLABEL;
+    }
+
+fprintf(stderr, "numMetaDataSelects: %d: %d\n", numMetaDataSelects, numMetaDataNonEmpty);
+
 if(metaDbExists)
     {
-    int len;
+    int i;
     char **metaValues = NULL;
     int count = metaDbVars(conn, &metaValues);
 
-    hPrintf("<tr><td>and</td>\n");
-    hPrintf("<td><b>Antibody</b></td><td>is</td>\n<td>\n");
-    len = getTermList(conn, &terms, "antibody");
-    cgiMakeDropListFull("hgt.antibodySearch", terms, terms, len, antibodySearch, NULL);
-    hPrintf("</td></tr>\n");
+    for(i = 0; i < numMetaDataSelects; i++)
+        {
+        char **terms;
+        char buf[256];
+        int len;
 
     hPrintf("<tr><td>and</td>\n");
     hPrintf("</td><td>\n");
-    cgiMakeDropListClassWithStyleAndJavascript("hgt.metaName", metaValues, count, metaName, 
+        safef(buf, sizeof(buf), "%s%i", METADATA_NAME_PREFIX, i + 1);
+        cgiMakeDropListClassWithStyleAndJavascript(buf, metaValues, count, metaDataName[i], 
                                                NULL, NULL, "onchange=metaPulldownChanged(this)");
     hPrintf("</td><td>is</td>\n<td>\n");
-    len = getTermList(conn, &terms, metaName);
-    cgiMakeDropListFull("hgt.metaSearch", terms, terms, len, metaSearch, NULL);
+        len = getTermList(conn, &terms, metaDataName[i]);
+        safef(buf, sizeof(buf), "%s%i", METADATA_VALUE_PREFIX, i + 1);
+        cgiMakeDropListFull(buf, terms, terms, len, metaDataValue[i], NULL);
     hPrintf("</td></tr>\n");
     }
+    }
 
 hPrintf("</table>\n");
 
 hPrintf("<input type='submit' name='%s' value='Search'>\n", searchTracks);
@@ -268,32 +316,25 @@
 if(descSearch != NULL && !strlen(descSearch))
     descSearch = NULL;
 if(groupSearch != NULL && sameString(groupSearch, ANYLABEL))
     groupSearch = NULL;
-if(metaSearch != NULL && (!strlen(metaSearch) || sameString(metaSearch, ANYLABEL)))
-    metaSearch = NULL;
-if(antibodySearch != NULL && sameString(antibodySearch, ANYLABEL))
-    antibodySearch = NULL;
-if((nameSearch != NULL && strlen(nameSearch)) || descSearch != NULL || groupSearch != NULL || metaSearch != NULL || antibodySearch != NULL)
+if(doSearch && ((nameSearch != NULL && strlen(nameSearch)) || descSearch != NULL || groupSearch != NULL || numMetaDataNonEmpty))
     {
     // First do the metaDb searches, which can be done quickly for all tracks with db queries.
     struct hash *matchingTracks = newHash(0);
     struct hash *trackMetadata = newHash(0);
     struct slName *el, *metaTracks = NULL;
-    boolean checkMeta = FALSE;
-    if(antibodySearch != NULL)
+    int i;
+    for(i = 0; i < numMetaDataSelects; i++)
         {
-        metaTracks = metaDbSearch(conn, "antibody", antibodySearch, "is");
-        checkMeta++;
-        }
-    if(metaSearch != NULL && strlen(metaSearch) && metaName != NULL && strlen(metaName))
+        if(!isEmpty(metaDataValue[i]))
         {
-        struct slName *tmp = metaDbSearch(conn, metaName, metaSearch, metaOp);
+            struct slName *tmp = metaDbSearch(conn, metaDataName[i], metaDataValue[i], "is");
         if(metaTracks == NULL)
             metaTracks = tmp;
         else
             metaTracks = slNameIntersection(metaTracks, tmp);
-        checkMeta++;
+            }
         }
     for (el = metaTracks; el != NULL; el = el->next)
         hashAddInt(matchingTracks, el->name, 1);
 
@@ -324,9 +365,9 @@
                     {
                     struct track *track = tr->track;
                     if((isEmpty(nameSearch) || isNameMatch(track, nameSearch, nameOp)) && 
                        (isEmpty(descSearch) || isDescriptionMatch(track, descSearch, trackMetadata)) &&
-                       (!checkMeta || hashLookup(matchingTracks, track->track) != NULL))
+                       (!numMetaDataNonEmpty || hashLookup(matchingTracks, track->track) != NULL))
                         {
                         tracksFound++;
                         refAdd(&tracks, track);
                         }
@@ -336,9 +377,9 @@
                         for (subTrack = track->subtracks; subTrack != NULL; subTrack = subTrack->next)
                             {
                             if((isEmpty(nameSearch) || isNameMatch(subTrack, nameSearch, nameOp)) &&
                                (isEmpty(descSearch) || isDescriptionMatch(subTrack, descSearch, trackMetadata)) &&
-                               (!checkMeta || hashLookup(matchingTracks, subTrack->track) != NULL))
+                               (!numMetaDataNonEmpty || hashLookup(matchingTracks, subTrack->track) != NULL))
                                 {
                                 // XXXX to parent hash. - use tdb->parent instead.
                                 hashAdd(parents, subTrack->track, track);
                                 tracksFound++;
@@ -388,9 +429,9 @@
             hPrintf("</td></tr>\n");
             }
         hPrintf("</table>\n");
         hButton("submit", "save");
-        hPrintf("</form>\n");
+        hPrintf("\n</form>\n");
         } 
     else
         {
         hPrintf("<p>No tracks found</p>\n");