288c7b7d7573211134a3d2fdced0911324b9de70
max
  Fri Oct 18 04:13:03 2024 -0700
adding primer3 exon amp mode support to primer3 links, refs #34657

diff --git src/hg/cgilib/hgSeq.c src/hg/cgilib/hgSeq.c
index f0b6f9a..0922fe8 100644
--- src/hg/cgilib/hgSeq.c
+++ src/hg/cgilib/hgSeq.c
@@ -7,30 +7,33 @@
 #include "dnautil.h"
 #include "dnaseq.h"
 #include "dystring.h"
 #include "cart.h"
 #include "cheapcgi.h"
 #include "hdb.h"
 #include "web.h"
 #include "rmskOut.h"
 #include "fa.h"
 #include "genePred.h"
 #include "bed.h"
 #include "hgSeq.h"
 #include "trackHub.h"
 #include "hubConnect.h"
 
+// primer3 can design primers around exons like exonprimer, but needs some flanking
+// sequence around the transcript for the first and the last primer
+const int PRIMER3EXTEND = 200;
 
 int hgSeqChromSize(char *db, char *chromName)
 /* get chrom size if there's a database out there,
  * otherwise just return 0 */
 {
 int thisSize = 0;
 if (hDbExists(db))
     thisSize = hChromSize(db, chromName);
 return thisSize;
 }
 
 void hgSeqFeatureRegionOptions(struct cart *cart, boolean canDoUTR,
 			       boolean canDoIntrons)
 /* Print out HTML FORM entries for feature region options. */
 {
@@ -258,45 +261,48 @@
 static struct dyString *genPrimer3Exons(int rCount, unsigned *rStarts, unsigned *rSizes, boolean *exonFlags, int seqStart) 
 /* generate string with exon positions on transcript sequence, format <relExonStart>-<relExonEnd,... e.g. "10-20,30-40," and return.
  * Result must be freed.*/
 {
 struct dyString* exonStr = dyStringNew(1024);
 for (int i=0;  i < rCount;  i++)
 {
     int start = rStarts[i] - seqStart;
     int size  = rSizes[i];
     if (exonFlags[i])
         dyStringPrintf(exonStr, "%d-%d,", start, size);
 }
 return exonStr;
 }
 
-static void printPrimerForm(char *seq, char *exons, char *db, char strand, char *returnUrl) {
+static void printPrimerForm(char *seq, char *exons, char *db, char strand, char *returnUrl, int primer3Extend) {
     /* print the form to send arguments to primer3 and click the submit button */
     puts("<form id='primer3Form' method='POST' action='https://dev.primer3plus.com/primer3plus/api/v1/upload'>\n");
 
     printf("<input type='hidden' name='SEQUENCE_TEMPLATE' value='%s'>\n", seq);
     printf("<input type='hidden' name='P3P_GB_EXONS' value='%s'>\n", exons);
     printf("<input type='hidden' name='P3P_GB_DB' value='%s'>\n", db);
     printf("<input type='hidden' name='P3P_GB_ORIENTATION' value='%c'>\n", strand);
     printf("<input type='hidden' name='P3P_GB_RETURN_PATH' value='%s'>\n", returnUrl);
 
     char *chrom = cgiString("c");
     char *winLeft = cgiString("l");
     char *winRight = cgiString("r");
 
-    printf("<input type='hidden' name='P3P_GB_POSITION' value='%s:%s-%s'>\n", chrom, winLeft, winRight);
+    int chromStart = atoi(winLeft) - primer3Extend;
+    int chromEnd = atoi(winRight) + primer3Extend;
+
+    printf("<input type='hidden' name='P3P_GB_POSITION' value='%s:%d-%d'>\n", chrom, chromStart, chromEnd);
     puts("<input style='display:none' type='submit' value='Submit'>");
     puts("</form>");
     jsInline("document.getElementById(\"primer3Form\").submit();\n");
 }
 
 static void hgSeqConcatRegionsDb(char *db, char *chrom, int chromSize, char strand, char *name,
 			  int rCount, unsigned *rStarts, unsigned *rSizes,
 			  boolean *exonFlags, boolean *cdsFlags, boolean toPrimer3)
 /* Concatenate and print out dna for a series of regions. 
  *
  * If toPrimer3 is true, will send them to primer3, overriding most arguments to allow exon-exon primers..*/
 {
 // Note: this code use to generate different sequence ids if the global
 // database in hdb was different than the db parameter.  This functionality
 // has been removed since the global database was removed and it didn't
@@ -425,31 +431,31 @@
 	}
     memcpy(cSeq->dna+offset, rSeq->dna+start, size);
     offset += size;
     }
 
 assert(offset == cSeq->size);
 cSeq->dna[offset] = 0;
 freeDnaSeq(&rSeq);
 
 if (isRc && !toPrimer3)
     reverseComplement(cSeq->dna, cSeq->size);
 
 if (toPrimer3)
 {
     struct dyString* primer3Exons = genPrimer3Exons(rCount, rStarts, rSizes, exonFlags, seqStart);
-    printPrimerForm(cSeq->dna, primer3Exons->string, db, strand, hgAbsUrlCgi("hgTracks"));
+    printPrimerForm(cSeq->dna, primer3Exons->string, db, strand, hgAbsUrlCgi("hgTracks"), PRIMER3EXTEND);
     dyStringFree(&primer3Exons);
 }
 else
 {
     safef(recName, sizeof(recName),
           "%s_%s range=%s:%d-%d 5'pad=%d 3'pad=%d "
           "strand=%c repeatMasking=%s",
           hubConnectSkipHubPrefix(db), 
           hubConnectSkipHubPrefix(name),
           chrom, seqStart+1, seqEnd,
           padding5, padding3,
           (isRc ? '-' : '+'),
           (maskRep ? repMasking : "none"));
     faWriteNext(stdout, recName, cSeq->dna, cSeq->size);
     freeDnaSeq(&cSeq);
@@ -574,38 +580,38 @@
 
 boolean promoter   = cgiBoolean("hgSeq.promoter");
 boolean intron     = cgiBoolean("hgSeq.intron");
 boolean utrExon5   = cgiBoolean("hgSeq.utrExon5");
 boolean cdsExon    = cgiBoolean("hgSeq.cdsExon");
 boolean utrExon3   = cgiBoolean("hgSeq.utrExon3");
 boolean downstream = cgiBoolean("hgSeq.downstream");
 
 int promoterSize   = cgiOptionalInt("hgSeq.promoterSize", 0);
 int downstreamSize = cgiOptionalInt("hgSeq.downstreamSize", 0);
 char *granularity  = cgiOptionalString("hgSeq.granularity");
 
 boolean toPrimer3  = cgiBoolean("primer3");
 if (toPrimer3) 
 {
-    promoter = FALSE;
+    promoter = TRUE;
     utrExon5 = TRUE;
     intron = TRUE;
     cdsExon = TRUE;
     utrExon3 = TRUE;
-    downstream = FALSE;
-    downstreamSize = 0;
-    promoterSize = 0;
+    downstream = TRUE;
+    downstreamSize = PRIMER3EXTEND;
+    promoterSize = PRIMER3EXTEND;
     granularity = "gene";
 }
 
 boolean utrIntron3 = utrExon3 && intron;
 boolean utrIntron5 = utrExon5 && intron;
 boolean cdsIntron  = cdsExon && intron;
 
 boolean concatRegions = granularity && sameString("gene", granularity);
 boolean concatAdjacent = (cgiBooleanDefined("hgSeq.splitCDSUTR") &&
 			  (! cgiBoolean("hgSeq.splitCDSUTR")));
 
 if (toPrimer3)
     concatAdjacent = TRUE;
 
 boolean isCDS, doIntron;