9a49290fedc8b4f9c7a631dedcfe8ac3aa2b4cee
chinhli
Thu Oct 20 14:10:43 2011 -0700
merge conflict resolved
diff --git src/hg/hgTracks/searchTracks.c src/hg/hgTracks/searchTracks.c
index 449f41e..b8b1314 100644
--- src/hg/hgTracks/searchTracks.c
+++ src/hg/hgTracks/searchTracks.c
@@ -1,779 +1,773 @@
/* Track search code used by hgTracks CGI */
#include "common.h"
#include "search.h"
#include "hCommon.h"
#include "memalloc.h"
#include "obscure.h"
#include "dystring.h"
#include "hash.h"
#include "cheapcgi.h"
#include "hPrint.h"
#include "htmshell.h"
#include "cart.h"
#include "hgTracks.h"
#include "web.h"
#include "jksql.h"
#include "hdb.h"
#include "mdb.h"
#include "fileUi.h"
#include "trix.h"
#include "jsHelper.h"
#include "imageV2.h"
#define TRACK_SEARCH_FORM "trackSearch"
#define SEARCH_RESULTS_FORM "searchResults"
#define TRACK_SEARCH_CURRENT_TAB "tsCurTab"
#define TRACK_SEARCH_SIMPLE "tsSimple"
#define TRACK_SEARCH_ON_NAME "tsName"
#define TRACK_SEARCH_ON_TYPE "tsType"
#define TRACK_SEARCH_ON_GROUP "tsGroup"
#define TRACK_SEARCH_ON_DESCR "tsDescr"
#define TRACK_SEARCH_SORT "tsSort"
#define SUPPORT_SUBTRACKS_INHERIT_DESCRIPTION
static int gCmpGroup(const void *va, const void *vb)
/* Compare groups based on label. */
{
const struct group *a = *((struct group **)va);
const struct group *b = *((struct group **)vb);
return strcmp(a->label, b->label);
}
// Would like to do a radio button choice ofsorts
enum sortBy
{
sbRelevance=0,
sbAbc =1,
sbHierarchy=2,
};
static int gCmpTrackHierarchy(const void *va, const void *vb)
/* Compare tracks based on longLabel. */
{
const struct slRef *aa = *((struct slRef **)va);
const struct slRef *bb = *((struct slRef **)vb);
const struct track *a = ((struct track *) aa->val);
const struct track *b = ((struct track *) bb->val);
if ( tdbIsFolder(a->tdb) && !tdbIsFolder(b->tdb))
return -1;
else if (!tdbIsFolder(a->tdb) && tdbIsFolder(b->tdb))
return 1;
if ( tdbIsContainer(a->tdb) && !tdbIsContainer(b->tdb))
return -1;
else if (!tdbIsContainer(a->tdb) && tdbIsContainer(b->tdb))
return 1;
if (!tdbIsContainerChild(a->tdb) && tdbIsContainerChild(b->tdb))
return -1;
else if ( tdbIsContainerChild(a->tdb) && !tdbIsContainerChild(b->tdb))
return 1;
return strcasecmp(a->longLabel, b->longLabel);
}
static int gCmpTrack(const void *va, const void *vb)
/* Compare tracks based on longLabel. */
{
const struct slRef *aa = *((struct slRef **)va);
const struct slRef *bb = *((struct slRef **)vb);
const struct track *a = ((struct track *) aa->val);
const struct track *b = ((struct track *) bb->val);
return strcasecmp(a->longLabel, b->longLabel);
}
static void findTracksSort(struct slRef **pTrack, enum sortBy sortBy)
{
if (sortBy == sbHierarchy)
slSort(pTrack, gCmpTrackHierarchy);
else if (sortBy == sbAbc)
slSort(pTrack, gCmpTrack);
else
slReverse(pTrack);
}
static int getFormatTypes(char ***pLabels, char ***pTypes)
{
char *crudeTypes[] = {
ANYLABEL,
"bam",
"psl",
"chain",
"netAlign",
"maf",
"bed",
"bigBed",
"ctgPos",
"expRatio",
"genePred",
"broadPeak",
"narrowPeak",
"rmsk",
"bedGraph",
"bigWig",
"wig",
"wigMaf"
};
// Non-standard:
// type altGraphX
// type axt
// type bed5FloatScore
// type bed5FloatScoreWithFdr
// type chromGraph
// type clonePos
// type coloredExon
// type encodeFiveC
// type factorSource
// type ld2
// type logo
// type maf
// type sample
// type wigMafProt 0.0 1.0
char *nicerTypes[] = {
ANYLABEL,
"Alignment binary (bam) - binary SAM",
"Alignment Blast (psl) - Blast output",
"Alignment Chains (chain) - Pairwise alignment",
"Alignment Nets (netAlign) - Net alignments",
"Alignments (maf) - multiple alignment format",
"bed - browser extensible data",
"bigBed - self index, often remote bed format",
"ctgPos - Contigs",
"expRatio - Expression ratios",
"Genes (genePred) - Gene prediction and annotation",
"Peaks Broad (broadPeak) - ENCODE large region peak format",
"Peaks Narrow (narrowPeak) - ENCODE small region peak format",
"Repeats (rmsk) - Repeat masking",
"Signal (bedGraph) - graphically represented bed data",
"Signal (bigWig) - self index, often remote wiggle format",
"Signal (wig) - wiggle format",
"Signal (wigMaf) - multiple alignment wiggle"
};
int ix = 0, count = sizeof(crudeTypes)/sizeof(char *);
char **labels;
char **values;
AllocArray(labels, count);
AllocArray(values, count);
for(ix=0;ix 0)
{
// Unfortunately trixSearch can't handle the slName list
int i;
char **trixWords = needMem(sizeof(char *) * trixWordCount);
for(i = 0, el = trixList; el != NULL; i++, el = el->next)
trixWords[i] = strLower(el->name);
// Now open the trix file
char trixFile[HDB_MAX_PATH_STRING];
getSearchTrixFile(database, trixFile, sizeof(trixFile));
struct trix *trix = trixOpen(trixFile);
struct trixSearchResult *tsList;
for(tsList = trixSearch(trix, trixWordCount, trixWords, TRUE); tsList != NULL; tsList = tsList->next)
{
struct track *track = (struct track *) hashFindVal(trackHash, tsList->itemId);
if (track != NULL) // It is expected that this is NULL (e.g. when the trix references trackDb tracks which have no tables)
{
refAdd(&tracks, track);
}
}
//trixClose(trix); // don't bother (this is a CGI that is about to end)
}
}
return tracks;
}
static struct slRef *advancedSearchForTracks(struct sqlConnection *conn,struct group *groupList,
char *nameSearch, char *typeSearch, char *descSearch,
char *groupSearch, struct slPair *mdbPairs)
// Performs the advanced search and returns the found tracks.
{
int tracksFound = 0;
struct slRef *tracks = NULL;
int numMetadataNonEmpty = 0;
struct slPair *pair = mdbPairs;
for (; pair!= NULL;pair=pair->next)
{
if (!isEmpty((char *)(pair->val)))
numMetadataNonEmpty++;
}
if(!isEmpty(groupSearch) && sameString(groupSearch,ANYLABEL))
groupSearch = NULL;
if(!isEmpty(typeSearch) && sameString(typeSearch,ANYLABEL))
typeSearch = NULL;
if(isEmpty(nameSearch) && isEmpty(typeSearch) && isEmpty(descSearch)
&& isEmpty(groupSearch) && numMetadataNonEmpty == 0)
return NULL;
// First do the metaDb searches, which can be done quickly for all tracks with db queries.
struct hash *matchingTracks = NULL;
if (numMetadataNonEmpty)
{
struct mdbObj *mdbObj, *mdbObjs = mdbObjRepeatedSearch(conn,mdbPairs,TRUE,FALSE);
if (mdbObjs)
{
for (mdbObj = mdbObjs; mdbObj != NULL; mdbObj = mdbObj->next)
{
if (matchingTracks == NULL)
matchingTracks = newHash(0);
hashAddInt(matchingTracks, mdbObj->obj, 1);
}
mdbObjsFree(&mdbObjs);
}
if (matchingTracks == NULL)
return NULL;
}
// Set the word lists up once
struct slName *nameList = NULL;
if (!isEmpty(nameSearch))
nameList = slNameListOfUniqueWords(cloneString(nameSearch),TRUE); // TRUE means respect quotes
struct slName *descList = NULL;
if (!isEmpty(descSearch))
descList = slNameListOfUniqueWords(cloneString(descSearch),TRUE);
struct group *group;
for (group = groupList; group != NULL; group = group->next)
{
if(isEmpty(groupSearch) || sameString(group->name, groupSearch))
{
if (group->trackList == NULL)
continue;
struct trackRef *tr;
for (tr = group->trackList; tr != NULL; tr = tr->next)
{
struct track *track = tr->track;
char *trackType = cloneFirstWord(track->tdb->type); // will be spilled
if ((matchingTracks == NULL || hashLookup(matchingTracks, track->track) != NULL)
&& (isEmpty(typeSearch) || (sameWord(typeSearch, trackType) && !tdbIsComposite(track->tdb)))
&& (isEmpty(nameSearch) || searchNameMatches(track->tdb, nameList))
&& (isEmpty(descSearch) || searchDescriptionMatches(track->tdb, descList)))
{
if (track != NULL)
{
tracksFound++;
refAdd(&tracks, track);
}
else
warn("found group track is NULL.");
}
if (track->subtracks != NULL)
{
struct track *subTrack;
for (subTrack = track->subtracks; subTrack != NULL; subTrack = subTrack->next)
{
trackType = cloneFirstWord(subTrack->tdb->type); // will be spilled
if ((matchingTracks == NULL || hashLookup(matchingTracks, subTrack->track) != NULL)
&& (isEmpty(typeSearch) || sameWord(typeSearch, trackType))
&& (isEmpty(nameSearch) || searchNameMatches(subTrack->tdb, nameList))
#ifdef SUPPORT_SUBTRACKS_INHERIT_DESCRIPTION
&& (isEmpty(descSearch)
|| searchDescriptionMatches(subTrack->tdb, descList)
|| (tdbIsCompositeChild(subTrack->tdb) && subTrack->parent
&& searchDescriptionMatches(subTrack->parent->tdb, descList))))
#else///ifndef SUPPORT_SUBTRACKS_INHERIT_DESCRIPTION
&& (isEmpty(descSearch) || searchDescriptionMatches(subTrack->tdb, descList)))
#endif///ndef SUPPORT_SUBTRACKS_INHERIT_DESCRIPTION
{
if (track != NULL)
{
tracksFound++;
refAdd(&tracks, subTrack);
}
else
warn("found subtrack is NULL.");
}
}
}
}
}
}
return tracks;
}
#define MAX_FOUND_TRACKS 100
static void findTracksPageLinks(int tracksFound, int startFrom)
{
if (tracksFound <= MAX_FOUND_TRACKS)
return;
// Opener
int willStartAt = 0;
int curPage = (startFrom/MAX_FOUND_TRACKS) + 1;
int endAt = startFrom+MAX_FOUND_TRACKS;
if (endAt > tracksFound)
endAt = tracksFound;
hPrintf("Listing %d - %d of %d tracks ",startFrom+1,endAt,tracksFound);
// << and <
if (startFrom >= MAX_FOUND_TRACKS)
{
hPrintf("« ",
TRACK_SEARCH,TRACK_SEARCH_PAGER,TRACK_SEARCH_PAGER);
willStartAt = startFrom - MAX_FOUND_TRACKS;
hPrintf(" ",
TRACK_SEARCH,TRACK_SEARCH_PAGER,willStartAt,TRACK_SEARCH_PAGER,willStartAt);
}
// page number links
int lastPage = (tracksFound/MAX_FOUND_TRACKS);
if ((tracksFound % MAX_FOUND_TRACKS) > 0)
lastPage++;
int thisPage = curPage - 3; // Window of 3 pages above and below
if (thisPage < 1)
thisPage = 1;
for (;thisPage <= lastPage && thisPage <= curPage + 3; thisPage++)
{
if (thisPage != curPage)
{
willStartAt = ((thisPage - 1) * MAX_FOUND_TRACKS);
endAt = willStartAt+ MAX_FOUND_TRACKS;
if (endAt > tracksFound)
endAt = tracksFound;
hPrintf(" %d ",
TRACK_SEARCH,TRACK_SEARCH_PAGER,willStartAt,thisPage,willStartAt+1,endAt,TRACK_SEARCH_PAGER,willStartAt,thisPage);
}
else
hPrintf(" %d ",COLOR_DARKGREY,thisPage);
}
// > and >>
if ((startFrom + MAX_FOUND_TRACKS) < tracksFound)
{
willStartAt = startFrom + MAX_FOUND_TRACKS;
hPrintf(" ",
TRACK_SEARCH,TRACK_SEARCH_PAGER,willStartAt,TRACK_SEARCH_PAGER,willStartAt);
willStartAt = tracksFound - (tracksFound % MAX_FOUND_TRACKS);
if (willStartAt == tracksFound)
willStartAt -= MAX_FOUND_TRACKS;
hPrintf(" »\n",
TRACK_SEARCH,TRACK_SEARCH_PAGER,willStartAt,TRACK_SEARCH_PAGER,willStartAt);
}
}
static void displayFoundTracks(struct cart *cart, struct slRef *tracks, int tracksFound,enum sortBy sortBy)
// Routine for displaying found tracks
{
hPrintf("\n"); // This div allows the clear button to empty it
if(tracksFound < 1)
{
hPrintf("
No tracks found
\n");
}
else
{
struct hash *tdbHash = makeTrackHash(database, chromName);
hPrintf("
\n");
// be done with json
jsonTdbSettingsUse(jsonTdbVars);
}
hPrintf("
"); // This div allows the clear button to empty it
}
void doSearchTracks(struct group *groupList)
{
-if (!advancedJavascriptFeaturesEnabled(cart))
- {
- warn("Requires advanced javascript features.");
- return;
- }
-
webIncludeResourceFile("ui.dropdownchecklist.css");
jsIncludeFile("ui.dropdownchecklist.js",NULL);
// This line is needed to get the multi-selects initialized
#ifdef NEW_JQUERY
jsIncludeFile("ddcl.js",NULL);
hPrintf("\n");
#else///ifndef NEW_JQUERY
hPrintf("\n");
hPrintf("\n");
#endif///ndef NEW_JQUERY
struct group *group;
char *groups[128];
char *labels[128];
int numGroups = 1;
groups[0] = ANYLABEL;
labels[0] = ANYLABEL;
char *nameSearch = cartOptionalString(cart, TRACK_SEARCH_ON_NAME);
char *typeSearch = cartUsualString( cart, TRACK_SEARCH_ON_TYPE,ANYLABEL);
char *simpleEntry = cartOptionalString(cart, TRACK_SEARCH_SIMPLE);
char *descSearch = cartOptionalString(cart, TRACK_SEARCH_ON_DESCR);
char *groupSearch = cartUsualString( cart, TRACK_SEARCH_ON_GROUP,ANYLABEL);
boolean doSearch = sameString(cartOptionalString(cart, TRACK_SEARCH), "Search") || cartUsualInt(cart, TRACK_SEARCH_PAGER, -1) >= 0;
struct sqlConnection *conn = hAllocConn(database);
boolean metaDbExists = sqlTableExists(conn, "metaDb");
int tracksFound = 0;
boolean searchTermsExist = FALSE;
int cols;
char buf[512];
char *currentTab = cartUsualString(cart, TRACK_SEARCH_CURRENT_TAB, "simpleTab");
enum searchTab selectedTab = (sameString(currentTab, "advancedTab") ? advancedTab : simpleTab);
if(selectedTab == simpleTab && !isEmpty(simpleEntry)) // NOTE: could support quotes in simple tab by detecting quotes and choosing to use doesNameMatch() || doesDescriptionMatch()
stripChar(simpleEntry, '"');
trackList = getTrackList(&groupList, -2); // global
makeGlobalTrackHash(trackList);
// NOTE: This is necessary when container cfg by '*' results in vis changes
// This will handle composite/view override when subtrack specific vis exists, AND superTrack reshaping.
parentChildCartCleanup(trackList,cart,oldVars); // Subtrack settings must be removed when composite/view settings are updated
slSort(&groupList, gCmpGroup);
for (group = groupList; group != NULL; group = group->next)
{
groupTrackListAddSuper(cart, group);
if (group->trackList != NULL)
{
groups[numGroups] = cloneString(group->name);
labels[numGroups] = cloneString(group->label);
numGroups++;
if (numGroups >= ArraySize(groups))
internalErr();
}
}
safef(buf, sizeof(buf),"Search for Tracks in the %s %s Assembly", organism, hFreezeFromDb(database));
webStartWrapperDetailedNoArgs(cart, database, "", buf, FALSE, FALSE, FALSE, FALSE);
hPrintf("");
hPrintf("
\n");
hPrintf("
"); // Restricts to max-width:1000px;
cgiDown(0.8);
if (measureTiming)
measureTime("Rendered tabs");
if(doSearch)
{
// Now search
struct slRef *tracks = NULL;
if(selectedTab==simpleTab && !isEmpty(simpleEntry))
tracks = simpleSearchForTracksstruct(simpleEntry);
else if(selectedTab==advancedTab)
tracks = advancedSearchForTracks(conn,groupList,nameSearch,typeSearch,descSearch,groupSearch,mdbSelects);
if (measureTiming)
measureTime("Searched for tracks");
// Sort and Print results
if(selectedTab!=filesTab)
{
enum sortBy sortBy = cartUsualInt(cart,TRACK_SEARCH_SORT,sbRelevance);
tracksFound = slCount(tracks);
if(tracksFound > 1)
findTracksSort(&tracks,sortBy);
displayFoundTracks(cart,tracks,tracksFound,sortBy);
if (measureTiming)
measureTime("Displayed found tracks");
}
slPairFreeList(&mdbSelects);
}
hFreeConn(&conn);
webNewSection("About Track Search");
if(metaDbExists)
hPrintf("Search for terms in track names, descriptions, groups, and ENCODE "
"metadata. If multiple terms are entered, only tracks with all terms "
"will be part of the results.");
else
hPrintf("
Search for terms in track descriptions, groups, and names. "
"If multiple terms are entered, only tracks with all terms "
"will be part of the results.");
hPrintf("
more help
\n");
// NOTE: Could use ajax and dynamically popup the html file in a box:
//hPrintf("
more help
\n");
// NOTE: OR by declaring a div and passing it to retrieveHtml, the html file could be embedded in the div.
// However, this is not desired here because of the titles.
//hPrintf("
more help\n");
//hPrintf("\n");
webEndSectionTables();
}