0cfb3c37fdb1de8873336f9d6f95b3422a465964 hiram Sun Aug 9 10:45:26 2026 -0700 in the case of an equivalent available browser for a user browser build request, alert the user to the existence of the equivalent refs #31811 diff --git src/hg/hubApi/findGenome.c src/hg/hubApi/findGenome.c index 0025898f646..ee2e3a5c772 100644 --- src/hg/hubApi/findGenome.c +++ src/hg/hubApi/findGenome.c @@ -538,77 +538,101 @@ jsonWriteNumber(jw, "availableAssemblies", universeCount); if (totalMatchCount > itemCount) jsonWriteBoolean(jw, "maxItemsLimit", TRUE); apiFinishOutput(0, NULL, jw); } else apiErrAbort(err400, err400Msg, "no genomes found matching search term %s='%s' for endpoint: /findGenome", argQ, inputSearchString); hDisconnectCentral(&conn); } void apiAssemblyRequest(char *words[MAX_PATH_INFO]) /* interface to the assemblySearch.html request form. Replaces the * primitive /cgi-bin/asr perl CGI script. Inserts a row into the - * ottoRequest table; the otto cron job watcher handles email. + * ottoRequest table; the otto cron job watcher handles email. When + * asmAlias already maps the requested asmId to an existing browser, + * that db goes into toDb instead of a duplicate asmId, which tells + * ottoRequest.py to send an "already available" acknowledgement + * instead of queuing a build. */ { char *extraArgs = verifyLegalArgs(argAssemblyRequest); if (extraArgs) apiErrAbort(err400, err400Msg, "extraneous arguments found for function /assemblyRequest '%s'", extraArgs); char *asmId = cgiOptionalString(argAsmId); char *name = cgiOptionalString(argName); char *email = cgiOptionalString(argEmail); char *betterName = cgiOptionalString(argBetterName); char *comment = cgiOptionalString(argComment); if (isEmpty(asmId) || isEmpty(name) || isEmpty(email)) apiErrAbort(err400, err400Msg, "must have arguments: %s, %s, %s for endpoint '/assemblyRequest'", argAsmId, argName, argEmail); /* Require a session cookie. Robots that have not * passed the challenge will not have one. */ char *cookieName = hUserCookie(); char *userId = findCookieData(cookieName); if (isEmpty(userId)) apiErrAbort(err400, err400Msg, "can not find required inputs for endpoint '/assemblyRequest'"); /* the ottoRequest table has no name/betterName columns, fold them into comment */ struct dyString *fullComment = dyStringNew(0); dyStringPrintf(fullComment, "name: '%s'", name); if (isNotEmpty(betterName)) dyStringPrintf(fullComment, "; betterName: '%s'", betterName); if (isNotEmpty(comment)) dyStringPrintf(fullComment, "; comment: '%s'", comment); -/* Record the request in the ottoRequest table (asmId placed in both fromDb - * and toDb in case toDb does not allow empty). Done locally (this host has - * hgcentral write grants) or relayed to genome.ucsc.edu (it doesn't) -- see - * inUcscEduDomain()/onGenomeRRMachine(). */ +/* If asmAlias already maps this asmId to a browser we have, there is + * nothing to build. asmAliasFind() returns its argument unchanged when + * no alias row matches, so differentString() is the standard way (see + * hubApi.c and hg/lib/web.c) to detect a real hit. */ +char *existingBrowser = asmAliasFind(asmId); +boolean alreadyExists = differentString(existingBrowser, asmId); + +/* Record the request in the ottoRequest table. For a normal request, + * asmId is placed in both fromDb and toDb (toDb otherwise unused for + * requestType='assembly', and left non-empty in case the column does + * not allow empty). When asmAlias resolves asmId to an existing + * browser, toDb carries that browser's db/asmId instead -- fromDb != + * toDb is then the signal that ottoRequest.py uses to send the "already + * available" acknowledgement and close out the row immediately instead + * of handing it to asmRequestWatch.sh, which would otherwise wait + * forever for a build that will never happen. Done locally (this host + * has hgcentral write grants) or relayed to genome.ucsc.edu (it + * doesn't) -- see inUcscEduDomain()/onGenomeRRMachine(). */ +char *toDb = alreadyExists ? existingBrowser : asmId; char *ottoStatus; if (inUcscEduDomain() && !onGenomeRRMachine()) - ottoStatus = relaySubmitOttoRequest("assembly", asmId, asmId, email, dyStringContents(fullComment)); + ottoStatus = relaySubmitOttoRequest("assembly", asmId, toDb, email, dyStringContents(fullComment)); else - ottoStatus = submitOttoRequest("assembly", asmId, asmId, email, dyStringContents(fullComment)); + ottoStatus = submitOttoRequest("assembly", asmId, toDb, email, dyStringContents(fullComment)); if (sameString(ottoStatus, "error")) apiErrAbort(err500, err500Msg, "internal error recording assembly request"); char nowTime[256]; time_t seconds = clock1(); struct tm *timeNow = localtime(&seconds); strftime(nowTime, sizeof nowTime, "%Y-%m-%d %H:%M:%S", timeNow); struct dyString *msg = dyStringNew(0); dyStringPrintf(msg, "%s\nAssembly request\nasmId: %s\nname: %s\nemail: %s\nbetterName: %s\ncomment: %s", nowTime, asmId, name, email, isNotEmpty(betterName) ? betterName : "", isNotEmpty(comment) ? comment : ""); +if (alreadyExists) + dyStringPrintf(msg, "\nnote: an equivalent browser for '%s' already exists as '%s'; " + "a confirmation email with a link is on its way instead of a new build", + asmId, existingBrowser); struct jsonWrite *jw = apiStartOutput(); jsonWriteString(jw, "msg", dyStringContents(msg)); +jsonWriteString(jw, "existingBrowser", alreadyExists ? existingBrowser : NULL); apiFinishOutput(0, NULL, jw); dyStringFree(&fullComment); dyStringFree(&msg); } /* void apiAssemblyRequest(char *words[MAX_PATH_INFO]) */