ef4a5ad1ea45eda4fde693f56f2106c553e87d93 chmalee Wed May 20 15:24:30 2026 -0700 Add cnv type to myVariants, refs #33808 diff --git src/hg/lib/myVariants.c src/hg/lib/myVariants.c index 02ddd3bf6cf..9829cf711f5 100644 --- src/hg/lib/myVariants.c +++ src/hg/lib/myVariants.c @@ -1,29 +1,58 @@ #include "common.h" #include "linefile.h" #include "dystring.h" #include "jksql.h" #include "myVariants.h" #include "myVariantsShare.h" #include "customTrack.h" #include "hdb.h" #include "hgConfig.h" #include "cheapcgi.h" #include "trashDir.h" #include "obscure.h" #include "wikiLink.h" +char *myVariantsItemTypes[] = { "transcript", "snv", "cnv" }; +int myVariantsNumItemTypes = ArraySize(myVariantsItemTypes); + +char *myVariantsCnvTypes[] = { + "deletion", "duplication", "insertion", "inversion", + "translocation", "complex", "breakend" +}; +int myVariantsNumCnvTypes = ArraySize(myVariantsCnvTypes); + +char *myVariantsCanonicalItemType(char *s) +/* Return the matching canonical entry from myVariantsItemTypes (case-insensitive), + * or NULL if s is empty or not in the allow-list. */ +{ +if (isEmpty(s)) + return NULL; +int ix = stringArrayIx(s, myVariantsItemTypes, myVariantsNumItemTypes); +return ix < 0 ? NULL : myVariantsItemTypes[ix]; +} + +char *myVariantsCanonicalCnvType(char *s) +/* Return the matching canonical entry from myVariantsCnvTypes (case-insensitive), + * or NULL if s is empty or not in the allow-list. */ +{ +if (isEmpty(s)) + return NULL; +int ix = stringArrayIx(s, myVariantsCnvTypes, myVariantsNumCnvTypes); +return ix < 0 ? NULL : myVariantsCnvTypes[ix]; +} + boolean isMyVariantsType(char *type) /* TRUE if type names the myVariants custom-track type. NULL-safe. */ { return sameOk(type, MYVARIANTS_TYPE); } boolean isMyVariantsTrack(char *trackName) /* TRUE if trackName is a myVariants custom track (own or shared). NULL-safe. */ { return trackName != NULL && startsWith(MYVARIANTS_TRACK_PREFIX, trackName); } boolean isMyVariantsSharedTrack(char *trackName) /* TRUE if trackName is a myVariants shared custom track. NULL-safe. */ { @@ -43,31 +72,33 @@ safecpy(ret->strand, sizeof(ret->strand), row[6]); ret->thickStart = sqlUnsigned(row[7]); ret->thickEnd = sqlUnsigned(row[8]); ret->itemRgb = sqlUnsigned(row[9]); ret->blockCount = sqlUnsigned(row[10]); sqlSignedDynamicArray(row[11], &ret->blockSizes, &sizeOne); assert(sizeOne == ret->blockCount); sqlSignedDynamicArray(row[12], &ret->chromStarts, &sizeOne); assert(sizeOne == ret->blockCount); ret->description = row[13]; ret->db = row[14]; ret->ref = row[15]; ret->alt = row[16]; ret->project = row[17]; ret->mouseover = row[18]; -ret->id = sqlUnsigned(row[19]); +ret->itemType = row[19]; +ret->cnvType = row[20]; +ret->id = sqlUnsigned(row[21]); } struct myVariants *myVariantsLoadByQuery(struct sqlConnection *conn, char *query) /* Load all myVariants from table that satisfy the query given. Dispose of this with myVariantsFreeList(). */ { struct myVariants *list = NULL, *el; struct sqlResult *sr; char **row; sr = sqlGetResult(conn, query); while ((row = sqlNextRow(sr)) != NULL) { el = myVariantsLoad(row); slAddHead(&list, el); } slReverse(&list); @@ -82,44 +113,46 @@ int i; for (i = 0; i < n; i++) dyStringPrintf(dy, "%d,", arr[i]); return dyStringCannibalize(&dy); } void myVariantsSaveToDb(struct sqlConnection *conn, struct myVariants *el, char *tableName, int updateSize) /* Save myVariants as a row to the table specified by tableName. * Uses explicit column names so custom fields in el->customFields are included. * If el->name is NULL or empty, fills it in post-INSERT as "Variant N" using * the row's auto-increment id; sqlLastAutoId wraps MariaDB's mysql_insert_id, * which is per-connection and unaffected by concurrent INSERTs on other * connections. */ { struct dyString *update = dyStringNew(updateSize); -sqlDyStringPrintf(update, "insert into %s (bin,chrom,chromStart,chromEnd,name,score,strand,thickStart,thickEnd,itemRgb,blockCount,blockSizes,chromStarts,description,db,ref,alt,project,mouseover", tableName); +sqlDyStringPrintf(update, "insert into %s (bin,chrom,chromStart,chromEnd,name,score,strand,thickStart,thickEnd,itemRgb,blockCount,blockSizes,chromStarts,description,db,ref,alt,project,mouseover,itemType,cnvType", tableName); /* Append custom field column names */ struct slPair *cf; for (cf = el->customFields; cf != NULL; cf = cf->next) sqlDyStringPrintf(update, ",%s", cf->name); char *insertName = isEmpty(el->name) ? "" : el->name; char *blockSizesStr = commaIntList(el->blockSizes, el->blockCount); char *chromStartsStr = commaIntList(el->chromStarts, el->blockCount); -sqlDyStringPrintf(update, ") values (%u,'%s',%u,%u,'%s',%u,'%s',%u,%u,%u,%u,'%s','%s','%s','%s','%s','%s','%s','%s'", +sqlDyStringPrintf(update, ") values (%u,'%s',%u,%u,'%s',%u,'%s',%u,%u,%u,%u,'%s','%s','%s','%s','%s','%s','%s','%s','%s','%s'", el->bin, el->chrom, el->chromStart, el->chromEnd, insertName, el->score, el->strand, el->thickStart, el->thickEnd, el->itemRgb, el->blockCount, blockSizesStr, chromStartsStr, - el->description, el->db, el->ref, el->alt, el->project, el->mouseover); + el->description, el->db, el->ref, el->alt, el->project, el->mouseover, + isEmpty(el->itemType) ? "snv" : el->itemType, + isEmpty(el->cnvType) ? "" : el->cnvType); freeMem(blockSizesStr); freeMem(chromStartsStr); /* Append custom field values */ for (cf = el->customFields; cf != NULL; cf = cf->next) sqlDyStringPrintf(update, ",'%s'", (char *)cf->val); sqlDyStringPrintf(update, ")"); sqlUpdate(conn, update->string); dyStringFree(&update); if (isEmpty(el->name)) { unsigned int newId = sqlLastAutoId(conn); struct dyString *nameUpdate = sqlDyStringCreate( @@ -149,31 +182,33 @@ safecpy(ret->strand, sizeof(ret->strand), row[6]); ret->thickStart = sqlUnsigned(row[7]); ret->thickEnd = sqlUnsigned(row[8]); ret->itemRgb = sqlUnsigned(row[9]); ret->blockCount = sqlUnsigned(row[10]); sqlSignedDynamicArray(row[11], &ret->blockSizes, &sizeOne); assert(sizeOne == ret->blockCount); sqlSignedDynamicArray(row[12], &ret->chromStarts, &sizeOne); assert(sizeOne == ret->blockCount); ret->description = cloneString(row[13]); ret->db = cloneString(row[14]); ret->ref = cloneString(row[15]); ret->alt = cloneString(row[16]); ret->project = cloneString(row[17]); ret->mouseover = cloneString(row[18]); -ret->id = sqlUnsigned(row[19]); +ret->itemType = cloneString(row[19]); +ret->cnvType = cloneString(row[20]); +ret->id = sqlUnsigned(row[21]); return ret; } struct myVariants *myVariantsLoadAll(char *fileName) /* Load all myVariants from a whitespace-separated file. Dispose of this with myVariantsFreeList(). */ { struct myVariants *list = NULL, *el; struct lineFile *lf = lineFileOpen(fileName, TRUE); char *row[MYVARIANTS_NUM_COLS]; while (lineFileRow(lf, row)) { el = myVariantsLoad(row); slAddHead(&list, el); } lineFileClose(&lf); @@ -228,50 +263,54 @@ int i; s = sqlEatChar(s, '{'); if (ret->blockCount > 0) AllocArray(ret->chromStarts, ret->blockCount); for (i=0; i<ret->blockCount; ++i) ret->chromStarts[i] = sqlSignedComma(&s); s = sqlEatChar(s, '}'); s = sqlEatChar(s, ','); } ret->description = sqlStringComma(&s); ret->db = sqlStringComma(&s); ret->ref = sqlStringComma(&s); ret->alt = sqlStringComma(&s); ret->project = sqlStringComma(&s); ret->mouseover = sqlStringComma(&s); +ret->itemType = sqlStringComma(&s); +ret->cnvType = sqlStringComma(&s); ret->id = sqlUnsignedComma(&s); *pS = s; return ret; } void myVariantsFree(struct myVariants **pEl) /* Free a single dynamically allocated myVariants such as created with myVariantsLoad(). */ { struct myVariants *el; if ((el = *pEl) == NULL) return; freeMem(el->chrom); freeMem(el->name); freeMem(el->blockSizes); freeMem(el->chromStarts); freeMem(el->description); freeMem(el->db); freeMem(el->ref); freeMem(el->alt); freeMem(el->project); freeMem(el->mouseover); +freeMem(el->itemType); +freeMem(el->cnvType); slPairFreeValsAndList(&el->customFields); freez(pEl); } void myVariantsFreeList(struct myVariants **pList) /* Free a list of dynamically allocated myVariants's */ { struct myVariants *el, *next; for (el = *pList; el != NULL; el = next) { next = el->next; myVariantsFree(&el); } *pList = NULL; } @@ -341,30 +380,38 @@ fprintf(f, "%s", el->ref); if (sep == ',') fputc('"',f); fputc(sep,f); if (sep == ',') fputc('"',f); fprintf(f, "%s", el->alt); if (sep == ',') fputc('"',f); fputc(sep,f); if (sep == ',') fputc('"',f); fprintf(f, "%s", el->project); if (sep == ',') fputc('"',f); fputc(sep,f); if (sep == ',') fputc('"',f); fprintf(f, "%s", el->mouseover); if (sep == ',') fputc('"',f); fputc(sep,f); +if (sep == ',') fputc('"',f); +fprintf(f, "%s", el->itemType ? el->itemType : ""); +if (sep == ',') fputc('"',f); +fputc(sep,f); +if (sep == ',') fputc('"',f); +fprintf(f, "%s", el->cnvType ? el->cnvType : ""); +if (sep == ',') fputc('"',f); +fputc(sep,f); fprintf(f, "%u", el->id); fputc(lastSep,f); } static char *myVariantsAutoSqlString = "table myVariants\n" "\"An item in a myVariants type track.\"\n" " (\n" " uint bin; \"Bin for range index\"\n" " string chrom; \"Reference sequence chromosome or scaffold\"\n" " uint chromStart;\"Start position in chromosome\"\n" " uint chromEnd; \"End position in chromosome\"\n" " string name; \"Name of item - up to 16 chars\"\n" " uint score; \"0-1000. Higher numbers are darker.\"\n" " char[1] strand; \"+ or - for strand\"\n" @@ -646,30 +693,32 @@ " name varchar(255) not null,\n" " score int unsigned not null,\n" " strand char(1) not null,\n" " thickStart int unsigned not null,\n" " thickEnd int unsigned not null,\n" " itemRgb int unsigned not null,\n" " blockCount int unsigned not null,\n" " blockSizes longblob not null,\n" " chromStarts longblob not null,\n" " description longblob not null,\n" " db varchar(255) not null,\n" " ref varchar(255) not null,\n" " alt varchar(255) not null,\n" " project varchar(255) not null,\n" " mouseover varchar(255) not null,\n" + " itemType varchar(16) not null default 'snv',\n" + " cnvType varchar(32) not null default '',\n" " id int auto_increment,\n" " PRIMARY KEY(id),\n" " INDEX(chrom(16),bin),\n" " INDEX(db),\n" " INDEX(project)\n" ") ENGINE=InnoDB;", db, tableName); sqlUpdate(conn, dyStringCannibalize(&createTable)); return myVariantsGetDbTable(userName); } } static void readLabelsFromCtFile(char *path, char *trackName, char **retShort, char **retLong) /* Parse the matching track line in an existing ctfile and pull shortLabel @@ -1014,31 +1063,31 @@ sqlSafef(query, sizeof(query), "SELECT DISTINCT project FROM %s WHERE project IS NOT NULL AND project != '' ORDER BY project", dbTable); projects = sqlQuickList(conn, query); hFreeConn(&conn); return projects; } /* Built-in column names from myVariants.as - any column NOT in this list is a * user-added custom column. Filtering by name (rather than index) is robust * against column reordering or future schema changes. */ static const char *builtInColumns[] = { "bin", "chrom", "chromStart", "chromEnd", "name", "score", "strand", "thickStart", "thickEnd", "itemRgb", "blockCount", "blockSizes", "chromStarts", "description", "db", "ref", "alt", "project", - "mouseover", "id", + "mouseover", "itemType", "cnvType", "id", }; static boolean isBuiltInColumn(char *name) { int i; for (i = 0; i < ArraySize(builtInColumns); i++) if (sameString(name, builtInColumns[i])) return TRUE; return FALSE; } struct slName *myVariantsGetCustomFields(char *userName) /* Return list of user-added custom column names for this user's myVariants table. * Excludes built-in columns and _hidden_ prefixed columns. * Caller must slFreeList the result. Returns NULL if no custom fields or table doesn't exist. */