e62655df7abb8c2ab44f5b11798f6f6c731afded
angie
  Wed Oct 5 15:42:05 2011 -0700
Feature #3710 (vcfTabix UI options): added optional filter on min QUAL score.
diff --git src/hg/hgTracks/vcfTrack.c src/hg/hgTracks/vcfTrack.c
index 8617c79..14ab5c6 100644
--- src/hg/hgTracks/vcfTrack.c
+++ src/hg/hgTracks/vcfTrack.c
@@ -6,68 +6,99 @@
 #include "errCatch.h"
 #include "hacTree.h"
 #include "hdb.h"
 #include "hgTracks.h"
 #include "pgSnp.h"
 #include "trashDir.h"
 #include "vcf.h"
 #include "vcfUi.h"
 #if (defined USE_TABIX && defined KNETFILE_HOOKS)
 #include "knetUdc.h"
 #include "udc.h"
 #endif//def USE_TABIX && KNETFILE_HOOKS
 
 #ifdef USE_TABIX
 
-static boolean getFilterValues(struct trackDb *tdb, struct slName **retValues)
+static boolean getMinQual(struct trackDb *tdb, double *retMinQual, boolean compositeLevel)
+/* Return TRUE and set retMinQual id cart contains minimum QUAL filter */
+{
+char cartVar[512];
+safef(cartVar, sizeof(cartVar), "%s."VCF_APPLY_MIN_QUAL_VAR, tdb->track);
+if (cartUsualBooleanClosestToHome(cart, tdb, compositeLevel,
+				  VCF_APPLY_MIN_QUAL_VAR, VCF_DEFAULT_APPLY_MIN_QUAL))
+    {
+    if (retMinQual != NULL)
+	*retMinQual = cartUsualDoubleClosestToHome(cart, tdb, compositeLevel, VCF_MIN_QUAL_VAR,
+						   VCF_DEFAULT_MIN_QUAL);
+    return TRUE;
+    }
+return FALSE;
+}
+
+static boolean minQualFail(struct vcfRecord *record, double minQual)
+/* Return TRUE if record's QUAL column value is non-numeric or is less than minQual. */
+{
+if (isEmpty(record->qual) ||
+    (record->qual[0] != '-' && !isdigit(record->qual[0])) ||
+    atof(record->qual) < minQual)
+    return TRUE;
+return FALSE;
+}
+
+static boolean getFilterValues(struct trackDb *tdb, struct slName **retValues,
+			       boolean compositeLevel)
 /* Return TRUE and set retValues if cart contains FILTER column values to exclude */
 {
 char cartVar[512];
-safef(cartVar, sizeof(cartVar), "%s.exclude_filterColumn", tdb->track);
+safef(cartVar, sizeof(cartVar), "%s."VCF_EXCLUDE_FILTER_VAR, tdb->track);
 if (cartListVarExists(cart, cartVar))
     {
     struct slName *selectedValues = cartOptionalSlNameList(cart, cartVar);
     if (retValues != NULL)
 	*retValues = selectedValues;
     return TRUE;
     }
 return FALSE;
 }
 
-static boolean excludeRecord(struct vcfRecord *record, struct slName *filterValues)
+static boolean filterColumnFail(struct vcfRecord *record, struct slName *filterValues)
 /* Return TRUE if record's FILTER column value(s) matches one of filterValues (from cart). */
 {
 int i;
 for (i = 0;  i < record->filterCount;  i++)
     if (slNameInList(filterValues, record->filters[i]))
 	return TRUE;
 return FALSE;
 }
 
 static void filterRecords(struct vcfFile *vcff, struct trackDb *tdb)
 /* If a filter is specified in the cart, remove any records that don't pass filter. */
 {
+boolean compositeLevel = isNameAtCompositeLevel(tdb, tdb->track);
+double minQual = 0;
 struct slName *filterValues = NULL;
-boolean gotFilter = getFilterValues(tdb, &filterValues);
-if (!gotFilter)
+boolean gotQualFilter = getMinQual(tdb, &minQual, compositeLevel);
+boolean gotFilterFilter = getFilterValues(tdb, &filterValues, compositeLevel);
+if (! (gotQualFilter || gotFilterFilter) )
     return;
 
 struct vcfRecord *rec, *nextRec, *newList = NULL;
 for (rec = vcff->records;  rec != NULL;  rec = nextRec)
     {
     nextRec = rec->next;
-    if (!excludeRecord(rec, filterValues))
+    if (! ((gotQualFilter && minQualFail(rec, minQual)) ||
+	   (gotFilterFilter && filterColumnFail(rec, filterValues))) )
 	slAddHead(&newList, rec);
     }
 slReverse(&newList);
 vcff->records = newList;
 }
 
 static struct pgSnp *vcfFileToPgSnp(struct vcfFile *vcff, struct trackDb *tdb)
 /* Convert vcff's records to pgSnp; don't free vcff until you're done with pgSnp
  * because it contains pointers into vcff's records' chrom. */
 {
 struct pgSnp *pgsList = NULL;
 struct vcfRecord *rec;
 int maxLen = 33;
 for (rec = vcff->records;  rec != NULL;  rec = rec->next)
     {