533112afe2a2005e80cdb1f82904ea65032d4302 braney Sat Oct 2 11:37:34 2021 -0700 split hg/lib into two separate libaries, one only used by the cgis diff --git src/hg/cgilib/search.c src/hg/cgilib/search.c new file mode 100644 index 0000000..88dba26 --- /dev/null +++ src/hg/cgilib/search.c @@ -0,0 +1,356 @@ +// Search code which is shared between different CGIs: hgFileSearch and hgTracks(Track Search) + +/* Copyright (C) 2014 The Regents of the University of California + * See README in this or parent directory for licensing information. */ + +#include "search.h" +#include "cheapcgi.h" +#include "hdb.h" +#include "hgConfig.h" +#include "hPrint.h" +#include "trix.h" +#include "mdb.h" +#include "subText.h" +#include "jsHelper.h" + +void getSearchTrixFile(char *database, char *buf, int len) +// Fill-in the name of the track search trix file +{ +char *trixPath = cfgOptionDefault("browser.trixPath", "/gbdb/$db/trackDb.ix"); +struct subText *subList = subTextNew("$db", database); +subTextStatic(subList, trixPath, buf, len); +char *subBuf = hReplaceGbdb(buf); +safecpy(buf, len, subBuf); +freez(&subBuf); +} + +boolean isSearchTracksSupported(char *database, struct cart *cart) +// Return TRUE if searchTracks is supported for this database and javascript is supported too +{ +char trixFile[HDB_MAX_PATH_STRING]; +getSearchTrixFile(database, trixFile, sizeof(trixFile)); +char *trixPath = hReplaceGbdb(trixFile); +bool ret = udcExists(trixPath); +freez(&trixPath); +return ret; +} + +struct slPair *fileFormatSearchWhiteList() +// Gets the whitelist of approved file formats that is allowed for search +{ +char *crudeTypes[] = + { + "bam", // "bam.bai" is now alway selected with bam, + "tagAlign", + "bed.gz", + "bigBed", + "broadPeak", + "narrowPeak", + "fastq", + "bigWig", + "wig" // TODO: Add "other" category. TODO: make into multi-select + }; +char *nicerTypes[] = + { + "Alignment binary (bam) - binary SAM", // "Alignment binary index (bai) - binary SAM index", + "Alignment tags (tagAlign)", + "bed - browser extensible data", + "bigBed - self index, often remote bed format", + "Peaks Broad (broadPeak) - ENCODE large region peak format", + "Peaks Narrow (narrowPeak) - ENCODE small region peak format", + "Raw Sequence (fastq) - High throughput sequence format", + "Signal (bigWig) - self index, often remote wiggle format", + "Signal (wig) - wiggle format" + }; + +struct slPair *fileTypes = NULL; +int ix = 0, count = sizeof(crudeTypes)/sizeof(char *); +for (ix=0;ix 0) + { + char *dropDownHtml = cgiMakeSingleSelectDropList(name,fileTypes,selected, + ANYLABEL, NULL, event, javascript, style, NULL); + slPairFreeList(&fileTypes); + return dropDownHtml; + } +return NULL; +} + +struct slPair *mdbSelectPairs(struct cart *cart, struct slPair *mdbVars) +// Returns the current mdb vars and vals in the table of drop down selects +{ +// figure out how many metadata selects are visible. +int numMetadataSelects = 0; + +struct slPair *mdbSelectPairs = NULL; +if (mdbVars == NULL) + return 0; + +// Get the current number of rows in the table of mdb selects +for (;;) + { + 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++; + } + +// Requesting to add or delete any? +int delSearchSelect = cartUsualInt(cart, TRACK_SEARCH_DEL_ROW, 0); // 1-based row to delete +int addSearchSelect = cartUsualInt(cart, TRACK_SEARCH_ADD_ROW, 0); // 1-based row to insert after +if (delSearchSelect) + numMetadataSelects--; +if (addSearchSelect) + numMetadataSelects++; + +if (numMetadataSelects) + { + int ix; + char buf[256]; + for (ix = 0; ix < numMetadataSelects; ix++) + { + int offset; // used to handle additions/deletions + if (addSearchSelect > 0 && ix >= addSearchSelect) + offset = 0; // do nothing to offset (i.e. copy data from previous row) + else if (delSearchSelect > 0 && ix + 1 >= delSearchSelect) + offset = 2; + else + offset = 1; + safef(buf, sizeof(buf), "%s%d", METADATA_NAME_PREFIX, ix + offset); + char *var = cartOptionalString(cart, buf); + char *val = NULL; + + // We need to make sure var is valid in this assembly; if it isn't, reset it to "cell". + if (slPairFindVal(mdbVars,var) == NULL) + var = "cell"; + else + { + safef(buf, sizeof(buf), "%s%d", METADATA_VALUE_PREFIX, ix + offset); + enum cvSearchable searchBy = cvSearchMethod(var); + if (searchBy == cvSearchByMultiSelect) + { + // Multi-selects as comma delimited list of values + struct slName *vals = cartOptionalSlNameList(cart,buf); + if (vals) + { + val = slNameListToString(vals,','); // A comma delimited list of values + slNameFreeList(&vals); + } + } + else if (searchBy == cvSearchBySingleSelect + || searchBy == cvSearchByFreeText + || searchBy == cvSearchByWildList) + val = cloneString(cartUsualString(cart, buf,ANYLABEL)); + //else if (searchBy == cvSearchByDateRange || searchBy == cvSearchByIntegerRange) + // { + // // TO BE IMPLEMENTED + // } + + if (val != NULL && sameString(val, ANYLABEL)) + val = NULL; + } + slPairAdd(&mdbSelectPairs,var,val); // val already cloned + } + if (delSearchSelect > 0) + { + safef(buf, sizeof(buf), "%s%d", METADATA_NAME_PREFIX, numMetadataSelects + 1); + cartRemove(cart, buf); + safef(buf, sizeof(buf), "%s%d", METADATA_VALUE_PREFIX, numMetadataSelects + 1); + cartRemove(cart, buf); + } + } +else + { + // create defaults + slPairAdd(&mdbSelectPairs,"cell", NULL); + slPairAdd(&mdbSelectPairs,"antibody",NULL); + } + +slReverse(&mdbSelectPairs); +return mdbSelectPairs; +} + +char *mdbSelectsHtmlRows(struct sqlConnection *conn,struct slPair *mdbSelects, + struct slPair *mdbVars,int cols,boolean fileSearch) +// genereates the html for the table rows containing mdb var and val selects. +// Assume tableSearch unless fileSearch +{ +struct dyString *output = dyStringNew(1024); + +dyStringPrintf(output,"ENCODE terms" + "\n", cols,COLOR_DARKGREY); + +struct slPair *mdbSelect = mdbSelects; +int row = 0; +for (;mdbSelect != NULL; mdbSelect = mdbSelect->next) + { + char buf[256]; + char *dropDownHtml = NULL; + + char id[256]; + char javascript[1024]; + + #define PLUS_MINUS_BUTTON "" + #define PLUS_MINUS_BUTTON_JS "findTracks.mdbSelectPlusMinus(this,%d);" + #define ADD_PM_BUTTON(type,num,value) \ + safef(id, sizeof id, "%sButton%d", (type), (num)); \ + dyStringPrintf(output,PLUS_MINUS_BUTTON, id, (value), \ + ((value) == '+' ? "add another row after":"delete")); \ + safef(javascript, sizeof javascript, PLUS_MINUS_BUTTON_JS, (num)); \ + jsOnEventById("click", id, javascript); + + dyStringAppend(output,"\n"); + row++; + + if (slCount(mdbSelects) > 2 || row > 2) + { + ADD_PM_BUTTON("minus", row, '-') + } + else + dyStringAppend(output," "); + ADD_PM_BUTTON("plus", row, '+') + + dyStringAppend(output,"and \n"); + safef(buf, sizeof(buf), "%s%i", METADATA_NAME_PREFIX, row); + + // Left side select of vars + dropDownHtml = cgiMakeSingleSelectDropList(buf, mdbVars,mdbSelect->name, NULL,"mdbVar", + "change", "findTracks.mdbVarChanged(this);", "font-size:.9em;", NULL); + if (dropDownHtml) + { + dyStringAppend(output,dropDownHtml); + freeMem(dropDownHtml); + } + + // Right side select of vals + safef(buf, sizeof(buf), "%s%i", METADATA_VALUE_PREFIX, row); + enum cvSearchable searchBy = cvSearchMethod(mdbSelect->name); + if (searchBy == cvSearchBySingleSelect || searchBy == cvSearchByMultiSelect) + { + dyStringPrintf(output,"\nis%s\n\n", + row,(searchBy == cvSearchByMultiSelect?" among":""),buf); + struct slPair *pairs = mdbValLabelSearch(conn, mdbSelect->name, MDB_VAL_STD_TRUNCATION, + FALSE, !fileSearch, fileSearch); + // not tags, either a file or table search + if (slCount(pairs) > 0) + { + char *dropDownHtml = cgiMakeSelectDropList((searchBy == cvSearchByMultiSelect), + buf, pairs,mdbSelect->val, ANYLABEL,"mdbVal", + "change", "findTracks.mdbValChanged(this);", + "min-width:200px; font-size:.9em;", NULL); + if (dropDownHtml) + { + dyStringAppend(output,dropDownHtml); + freeMem(dropDownHtml); + } + slPairFreeList(&pairs); + } + } + else if (searchBy == cvSearchByFreeText) + { + dyStringPrintf(output,"contains\n\n",row,buf); + safef(id, sizeof id, "%i_change", row); + dyStringPrintf(output,"\n", + buf,id,(mdbSelect->val ? (char *)mdbSelect->val: "")); + jsOnEventById("change", id, "findTracks.mdbVarChanged(true);"); + } + else if (searchBy == cvSearchByWildList) + { + dyStringPrintf(output,"is among\n\n",row,buf); + safef(id, sizeof id, "%i_change", row); + dyStringPrintf(output,"\n", + buf,id,(mdbSelect->val ? (char *)mdbSelect->val: "")); + jsOnEventById("change", id, "findTracks.mdbVarChanged(true);"); + } + //else if (searchBy == cvSearchByDateRange || searchBy == cvSearchByIntegerRange) + // { + // // TO BE IMPLEMENTED + // } + dyStringPrintf(output," \n", row); + dyStringPrintf(output,"\n"); + } + + dyStringPrintf(output," ", cols); + +return dyStringCannibalize(&output); +} + + +static boolean searchMatchToken(char *string, char *token) +{ +// do this with regex ? Would require all sorts of careful parsing for ()., etc. +if (string == NULL) + return (token == NULL); +if (token == NULL) + return TRUE; + +if (!strchr(token,'*') && !strchr(token,'?')) + return (strcasestr(string,token) != NULL); + +char wordWild[1024]; +safef(wordWild,sizeof wordWild,"*%s*",token); +return wildMatch(wordWild, string); +} + +boolean searchNameMatches(struct trackDb *tdb, struct slName *wordList) +// returns TRUE if all words in preparsed list matches short or long label +// A "word" can be "multiple words" (parsed from quoteed string). +{ +if (tdb->shortLabel == NULL || tdb->longLabel == NULL) + return (wordList != NULL); + +struct slName *word = wordList; +for (; word != NULL; word = word->next) + { + if (!searchMatchToken(tdb->shortLabel,word->name) + && !searchMatchToken(tdb->longLabel, word->name)) + return FALSE; + } +return TRUE; +} + +boolean searchDescriptionMatches(struct trackDb *tdb, struct slName *wordList) +// returns TRUE if all words in preparsed list matches html description page. +// A "word" can be "multiple words" (parsed from quoteed string). +// Because description contains html, quoted string match has limits. +// DANGER: this will alter html of tdb struct (replacing \n with ' ', +// so the html should not be displayed after. +{ +if (tdb->html == NULL) + return (wordList != NULL); + +if (strchr(tdb->html,'\n')) // DANGER: don't own memory. + strSwapChar(tdb->html,'\n',' '); // However, this CGI will use html for no other purpose + +struct slName *word = wordList; +for (; word != NULL; word = word->next) + { + if (!searchMatchToken(tdb->html,word->name)) + return FALSE; + } +return TRUE; +} +