Beispiel #1
0
static void listAssemblyHubs(struct jsonWrite *jw)
/* Write out JSON describing assembly hubs (not track-only hubs) connected in the cart. */
{
jsonWriteListStart(jw, "hubs");
struct hubConnectStatus *status, *statusList = hubConnectStatusListFromCart(cart);
for (status = statusList;  status != NULL;  status = status->next)
    {
    struct trackHub *hub = status->trackHub;
    if (hub == NULL)
        continue;
    int assemblyCount = trackHubCountAssemblies(hub);
    if (assemblyCount > 0)
        {
        jsonWriteObjectStart(jw, NULL);
        jsonWriteString(jw, "name", hub->name);
        jsonWriteString(jw, "shortLabel", hub->shortLabel);
        jsonWriteString(jw, "longLabel", hub->longLabel);
        jsonWriteString(jw, "defaultDb", trackHubDefaultAssembly(hub));
        jsonWriteString(jw, "hubUrl", status->hubUrl);
        jsonWriteNumber(jw, "assemblyCount", assemblyCount);
        jsonWriteString(jw, "errorMessage", status->errorMessage);
        // We might be able to do better than this for taxId, for example if defaultDb is local
        // or if hub genomes ever specify taxId...
        jsonWriteNumber(jw, "taxId", 0);
        jsonWriteObjectEnd(jw);
        }
    }
jsonWriteListEnd(jw);
}
void cartJsonExecute(struct cartJson *cj)
/* Get commands from cgi, print Content-type, execute commands, print results as JSON. */
{
cartJsonPushErrHandlers();
puts("Content-Type:text/javascript\n");

// Initialize response JSON object:
jsonWriteObjectStart(cj->jw, NULL);
// Always send back hgsid:
jsonWriteString(cj->jw, cartSessionVarName(), cartSessionId(cj->cart));

char *commandJson = cgiOptionalString(CARTJSON_COMMAND);
if (commandJson)
    {
    struct jsonElement *commandObj = jsonParse(commandJson);
    struct hash *commandHash = jsonObjectVal(commandObj, "commandObj");
    // change* commands need to go first!  Really we need an ordered map type here...
    // for now, just make a list and sort to put change commands at the front.
    struct slPair *commandList = NULL, *cmd;
    struct hashCookie cookie = hashFirst(commandHash);
    struct hashEl *hel;
    while ((hel = hashNext(&cookie)) != NULL)
        slAddHead(&commandList, slPairNew(hel->name, hel->val));
    slSort(&commandList, commandCmp);
    for (cmd = commandList;  cmd != NULL;  cmd = cmd->next)
	doOneCommand(cj, cmd->name, (struct jsonElement *)cmd->val);
    }

cartJsonPrintWarnings(cj->jw);
jsonWriteObjectEnd(cj->jw);
puts(cj->jw->dy->string);
cartJsonPopErrHandlers();
}
Beispiel #3
0
static void printActiveGenomes()
/* Print out JSON for an object mapping each genome that has at least one db with active=1
 * to its taxId.  */
{
struct jsonWrite *jw = jsonWriteNew();
jsonWriteObjectStart(jw, NULL);
struct sqlConnection *conn = hConnectCentral();
// Join with defaultDb because in rare cases, different taxIds (species vs. subspecies)
// may be used for different assemblies of the same species.  Using defaultDb means that
// we send a taxId consistent with the taxId of the assembly that we'll change to when
// the species is selected from the tree.
char *query = NOSQLINJ "select dbDb.genome, taxId, dbDb.name from dbDb, defaultDb "
    "where defaultDb.name = dbDb.name and active = 1 "
    "and taxId > 1;"; // filter out experimental hgwdev-only stuff with invalid taxIds
struct sqlResult *sr = sqlGetResult(conn, query);
char **row;
while ((row = sqlNextRow(sr)) != NULL)
    {
    char *genome = row[0], *db = row[2];
    int taxId = atoi(row[1]);
    if (hDbExists(db))
        jsonWriteNumber(jw, genome, taxId);
    }
hDisconnectCentral(&conn);
jsonWriteObjectEnd(jw);
puts(jw->dy->string);
jsonWriteFree(&jw);
}
Beispiel #4
0
void writeValLabelMatches(struct jsonWrite *jw, struct slPair *matches, char *category)
/* Append JSON objects containing alt/fix seqs with extra label info & optional category. */
{
struct slPair *match;
for (match = matches; match != NULL; match = match->next)
    {
    char *seqName = match->name;
    if (strchr(seqName, '_') && !endsWith(seqName, "_random") && !startsWith("chrUn", seqName))
        {
        jsonWriteObjectStart(jw, NULL);
        jsonWriteString(jw, "value", seqName);
        char *extraInfo = match->val;
        if (isNotEmpty(extraInfo))
            {
            int len = strlen(seqName) + strlen(extraInfo) + 32;
            char label[len];
            safef(label, sizeof label, "%s (%s)", seqName, extraInfo);
            jsonWriteString(jw, "label", label);
            }
        if (isNotEmpty(category))
            jsonWriteString(jw, "category", category);
        jsonWriteObjectEnd(jw);
        }
    }
}
static void printCladeOrgDbTree(struct jsonWrite *jw)
/* Print out the tree of clades, organisms and dbs as JSON.  Each node has value and label
 * for menu options; clade nodes and org nodes also have children and default. */
{
jsonWriteListStart(jw, "cladeOrgDb");
struct slPair *clade, *cladeOptions = hGetCladeOptions();
struct dbDb *centralDbDbList = hDbDbList();
for (clade = cladeOptions;  clade != NULL;  clade = clade->next)
    {
    jsonWriteObjectStart(jw, NULL);
    jsonWriteValueLabel(jw, clade->name, clade->val);
    jsonWriteListStart(jw, "children");
    struct slPair *org, *orgOptions = hGetGenomeOptionsForClade(clade->name);
    for (org = orgOptions;  org != NULL;  org = org->next)
        {
        jsonWriteObjectStart(jw, NULL);
        jsonWriteValueLabel(jw, org->name, org->val);
        jsonWriteListStart(jw, "children");
        struct dbDb *dbDb, *dbDbList;
        if (isHubTrack(org->name))
            dbDbList = trackHubGetDbDbs(clade->name);
        else
            dbDbList = centralDbDbList;
        for (dbDb = dbDbList;  dbDb != NULL;  dbDb = dbDb->next)
            {
            if (sameString(org->name, dbDb->genome))
                {
                jsonWriteObjectStart(jw, NULL);
                jsonWriteValueLabel(jw, dbDb->name, dbDb->description);
                jsonWriteString(jw, "defaultPos", dbDb->defaultPos);
                jsonWriteObjectEnd(jw);
                }
            }
        jsonWriteListEnd(jw);   // children (dbs)
        jsonWriteString(jw, "default", trimSpaces(hDefaultDbForGenome(org->name)));
        jsonWriteObjectEnd(jw); // org
        }
    jsonWriteListEnd(jw);   // children (orgs)
    jsonWriteString(jw, "default", trimSpaces(hDefaultGenomeForClade(clade->name)));
    jsonWriteObjectEnd(jw); // clade
    }
jsonWriteListEnd(jw);
}
Beispiel #6
0
static void writeDbDbMatch(struct jsonWrite *jw, struct dbDbMatch *match, char *term,
                           char *category)
/* Write out the JSON encoding of a match in dbDb. */
{
struct dbDb *dbDb = match->dbDb;
jsonWriteObjectStart(jw, NULL);
jsonWriteString(jw, "genome", dbDb->genome);
// label includes <b> tag to highlight the match for term.
char label[PATH_LEN*4];
// value is placed in the input box when user selects the item.
char value[PATH_LEN*4];
if (match->type == ddmtSciName)
    {
    safef(value, sizeof(value), "%s (%s)", dbDb->scientificName, dbDb->genome);
    char *bolded = boldTerm(dbDb->scientificName, term, match->offset, match->type);
    safef(label, sizeof(label), "%s (%s)", bolded, dbDb->genome);
    freeMem(bolded);
    }
else if (match->type == ddmtGenome)
    {
    safecpy(value, sizeof(value), dbDb->genome);
    char *bolded = boldTerm(dbDb->genome, term, match->offset, match->type);
    safecpy(label, sizeof(label), bolded);
    freeMem(bolded);
    }
else if (match->type == ddmtDb)
    {
    safecpy(value, sizeof(value), dbDb->name);
    char *bolded = boldTerm(dbDb->name, term, match->offset, match->type);
    safef(label, sizeof(label), "%s (%s %s)",
          bolded, dbDb->genome, dbDb->description);
    freeMem(bolded);
    jsonWriteString(jw, "db", dbDb->name);
    }
else if (match->type == ddmtDescription)
    {
    safef(value, sizeof(value), "%s (%s %s)",
          dbDb->name, dbDb->genome, dbDb->description);
    char *bolded = boldTerm(dbDb->description, term, match->offset, match->type);
    safef(label, sizeof(label), "%s (%s %s)",
          dbDb->name, dbDb->genome, bolded);
    freeMem(bolded);
    jsonWriteString(jw, "db", dbDb->name);
    }
else
    errAbort("writeDbDbMatch: unrecognized dbDbMatchType value %d (db %s, term %s)",
             match->type, dbDb->name, term);
jsonWriteString(jw, "label", label);
jsonWriteString(jw, "value", value);
jsonWriteNumber(jw, "taxId", dbDb->taxId);
if (isNotEmpty(category))
    jsonWriteString(jw, "category", category);
jsonWriteObjectEnd(jw);
}
Beispiel #7
0
void writeAltFixMatches(struct jsonWrite *jw, struct slName *matches, char *category)
/* Append JSON objects containing alt or fix patch sequence names & optional category. */
{
struct slName *match;
for (match = matches; match != NULL; match = match->next)
    {
    if (strchr(match->name, '_'))
        {
        jsonWriteObjectStart(jw, NULL);
        jsonWriteString(jw, "value", match->name);
        if (isNotEmpty(category))
            jsonWriteString(jw, "category", category);
        jsonWriteObjectEnd(jw);
        }
    }
}
static void getCladeOrgDbPos(struct cartJson *cj, struct hash *paramHash)
/* Get cart's current clade, org, db, position and geneSuggest track. */
{
jsonWriteObjectStart(cj->jw, "cladeOrgDb");
printCladeOrgDbTree(cj->jw);
char *db = cartString(cj->cart, "db");
jsonWriteString(cj->jw, "db", db);
char *org = cartUsualString(cj->cart, "org", hGenome(db));
jsonWriteString(cj->jw, "org", org);
char *clade = cartUsualString(cj->cart, "clade", hClade(org));
jsonWriteString(cj->jw, "clade", clade);
jsonWriteObjectEnd(cj->jw);
char *position = cartOptionalString(cj->cart, "position");
if (isEmpty(position))
    position = hDefaultPos(db);
jsonWriteString(cj->jw, "position", position);
printGeneSuggestTrack(cj, db);
}
Beispiel #9
0
struct jsonWrite *jsonForSoftware(struct eapSoftware *sw)
/* Return JSON text for software.  This is something that looks
 * like:
	{
	"name" : "macs2",
	"title" : "macs2",
	"url" : "https://github.com/taoliu/MACS/"
	}
 */
{
struct jsonWrite *jw = jsonWriteNew(0);
jsonWriteObjectStart(jw);
jsonWriteString(jw, "name", sw->name);
jsonWriteString(jw, "title", sw->name);
jsonWriteString(jw, "url", sw->url);
jsonWriteObjectEnd(jw);
return jw;
}
Beispiel #10
0
static void writeAssemblyHubMatches(struct jsonWrite *jw, struct aHubMatch *aHubMatchList)
/* Write out JSON for each assembly in each assembly hub that matched the search term. */
{
struct aHubMatch *aHubMatch;
for (aHubMatch = aHubMatchList;  aHubMatch != NULL;  aHubMatch = aHubMatch->next)
    {
    jsonWriteObjectStart(jw, NULL);
    jsonWriteString(jw, "genome", aHubMatch->shortLabel);
    jsonWriteString(jw, "db", aHubMatch->aDb);
    jsonWriteString(jw, "hubUrl", aHubMatch->hubUrl);
    jsonWriteString(jw, "hubName", hubNameFromUrl(aHubMatch->hubUrl));
    // Add a category label for customized autocomplete-with-categories.
    char category[PATH_LEN*4];
    safef(category, sizeof(category), "Assembly Hub: %s", aHubMatch->shortLabel);
    jsonWriteString(jw, "category", category);
    jsonWriteString(jw, "value", aHubMatch->aDb);
    // Use just the db as label, since shortLabel is included in the category label.
    jsonWriteString(jw, "label", aHubMatch->aDb);
    jsonWriteObjectEnd(jw);
    }
}
Beispiel #11
0
struct jsonWrite *jsonForSwVersion(struct sqlConnection *conn, struct eapSwVersion *ver)
/* Construct JSON string describing ver. */
{
char query[256];
sqlSafef(query, sizeof(query), "select * from eapSoftware where name='%s'", ver->software);
struct eapSoftware *sw = eapSoftwareLoadByQuery(conn, query);
if (sw == NULL || isEmpty(sw->metaUuid))
    {
    verbose(2, "Skipping %s %s because %s has not been registered with metadatabase\n", 
	ver->software, ver->version, ver->software);
    return NULL;
    }
uglyf("sw id %u, softare %s, metaUuid %s\n",  sw->id, sw->name, sw->metaUuid);
struct jsonWrite *jw = jsonWriteNew(0);
jsonWriteObjectStart(jw);
jsonWriteString(jw, "software", sw->metaUuid);
jsonWriteString(jw, "version", ver->version);
jsonWriteString(jw, "dcc_md5", ver->md5);
jsonWriteObjectEnd(jw);
return jw;
}
static boolean writeGroupedTrack(struct jsonWrite *jw, char *name, char *label,
                                 struct hash *fieldHash, struct hash *excludeTypesHash,
                                 int maxDepth, struct slRef *tdbRefList)
/* If tdbRefList is empty after excluding tracks/views/subtracks whose types are
 * in excludeTypesHash, then return FALSE and write nothing.  Otherwise write a group
 * and its tracks/views/subtracks and return TRUE. */
{
// Make a new jsonWrite object in case this group turns out to have no children after filtering.
struct jsonWrite *jwNew = jsonWriteNew();
jsonWriteObjectStart(jwNew, NULL);
jsonWriteString(jwNew, "name", name);
jsonWriteString(jwNew, "label", label);
jsonWriteListStart(jwNew, "tracks");
boolean gotSomething = FALSE;
struct slRef *tdbRef;
for (tdbRef = tdbRefList;  tdbRef != NULL;  tdbRef = tdbRef->next)
    {
    struct trackDb *tdb = tdbRef->val;
    // First see if there are any tracks to show for this group:
    struct jsonWrite *jwTrack = rTdbToJw(tdb, fieldHash, excludeTypesHash, 1, maxDepth);
    if (jwTrack)
        {
        gotSomething = TRUE;
        jsonWriteAppend(jwNew, NULL, jwTrack);
        jsonWriteFree(&jwTrack);
        }
    }
if (gotSomething)
    {
    // Group has at least one track, so append it to jw.
    jsonWriteListEnd(jwNew);
    jsonWriteObjectEnd(jwNew);
    jsonWriteAppend(jw, NULL, jwNew);
    }
jsonWriteFree(&jwNew);
return gotSomething;
}
static void hgPositionsJson(struct jsonWrite *jw, char *db, struct hgPositions *hgp, struct cart *cart)
/* Write out JSON description of multiple position matches. */
{
struct hgPosTable *table;
jsonWriteListStart(jw, "positionMatches");
struct trackDb *tdbList = NULL;
for (table = hgp->tableList; table != NULL; table = table->next)
    {
    if (table->posList != NULL)
	{
	char *tableName = table->name;
	// clear the tdb cache if this track is a hub track
	if (isHubTrack(tableName))
	    tdbList = NULL;
	struct trackDb *tdb = tdbForTrack(db, tableName, &tdbList);
	if (!tdb && startsWith("all_", tableName))
            tdb = tdbForTrack(db, tableName+strlen("all_"), &tdbList);
        if (!tdb)
            errAbort("no track for table \"%s\" found via a findSpec", tableName);
	char *trackName = tdb->track;
	jsonWriteObjectStart(jw, NULL);
	jsonWriteString(jw, "name", table->name);
	jsonWriteString(jw, "trackName", trackName);
	jsonWriteString(jw, "description", table->description);
	jsonWriteString(jw, "vis", hCarefulTrackOpenVis(db, trackName));
	jsonWriteListStart(jw, "matches");
	struct hgPos *pos;
	for (pos = table->posList; pos != NULL; pos = pos->next)
	    {
	    char *encMatches = cgiEncode(pos->browserName);
	    jsonWriteObjectStart(jw, NULL); // begin one match
	    if (pos->chrom != NULL)
		jsonWriteStringf(jw, "position", "%s:%d-%d",
				 pos->chrom, pos->chromStart+1, pos->chromEnd);
	    else
		// GenBank results set position to GB accession instead of chr:s-e position.
		jsonWriteString(jw, "position", pos->name);
	    // this is magic to tell the browser to make the
	    // composite and this subTrack visible
	    if (tdb->parent)
		{
		if (tdbIsSuperTrackChild(tdb))
		    jsonWriteStringf(jw, "extraSel", "%s=show&", tdb->parent->track);
		else
		    {
		    // tdb is a subtrack of a composite or a view
		    jsonWriteStringf(jw, "extraSel", "%s_sel=1&%s_sel=1&",
				     trackName, tdb->parent->track);
		    }
		}
	    jsonWriteString(jw, "hgFindMatches", encMatches);
	    jsonWriteString(jw, "posName", htmlEncodeText(pos->name, FALSE));
	    if (pos->description)
		{
		stripString(pos->description, "\n");
		jsonWriteString(jw, "description", stripAnchor(pos->description));
		}
	    jsonWriteObjectEnd(jw); // end one match
	    }
	jsonWriteListEnd(jw); // end matches
	jsonWriteObjectEnd(jw); // end one table
	}
    }
    jsonWriteListEnd(jw); // end positionMatches
}
void cartJsonGetGroupedTrackDb(struct cartJson *cj, struct hash *paramHash)
/* Translate trackDb list (only a subset of the fields) into JSON array of track group objects;
 * each group contains an array of track objects that may have subtracks.  Send it in a wrapper
 * object that includes the database from which it was taken; it's possible that by the time
 * this reaches the client, the user might have switched to a new db. */
{
struct jsonWrite *jw = cj->jw;
struct trackDb *fullTrackList = NULL;
struct grp *fullGroupList = NULL;
struct errCatch *errCatch = errCatchNew();
if (errCatchStart(errCatch))
    {
    cartTrackDbInit(cj->cart, &fullTrackList, &fullGroupList, /* useAccessControl=*/TRUE);
    }
errCatchEnd(errCatch);
if (errCatch->gotError)
    {
    warn("%s", errCatch->message->string);
    jsonWriteObjectStart(jw, "groupedTrackDb");
    jsonWriteString(jw, "db", cartString(cj->cart, "db"));
    jsonWriteListStart(jw, "groupedTrackDb");
    jsonWriteListEnd(jw);
    jsonWriteObjectEnd(jw);
    return;
    }
errCatchFree(&errCatch);
struct hash *groupedTrackRefList = hashTracksByGroup(fullTrackList);
// If the optional param 'fields' is given, hash the field names that should be returned.
char *fields = cartJsonOptionalParam(paramHash, "fields");
struct hash *fieldHash = hashFromCommaString(fields);
char *excludeTypes = cartJsonOptionalParam(paramHash, "excludeTypes");
struct hash *excludeTypesHash = hashFromCommaString(excludeTypes);
// Also check for optional parameter 'maxDepth':
int maxDepth = -1;
char *maxDepthStr = cartJsonOptionalParam(paramHash, "maxDepth");
if (isNotEmpty(maxDepthStr))
    maxDepth = atoi(maxDepthStr);
jsonWriteObjectStart(jw, "groupedTrackDb");
jsonWriteString(jw, "db", cartString(cj->cart, "db"));
jsonWriteListStart(jw, "groupedTrackDb");
int nonEmptyGroupCount = 0;
struct grp *grp;
for (grp = fullGroupList;  grp != NULL;  grp = grp->next)
    {
    struct slRef *tdbRefList = hashFindVal(groupedTrackRefList, grp->name);
    if (writeGroupedTrack(jw, grp->name, grp->label, fieldHash, excludeTypesHash,
                          maxDepth, tdbRefList))
        {
        nonEmptyGroupCount++;
        }
    }
if (nonEmptyGroupCount == 0)
    {
    // Catch-all for assembly hubs that don't declare groups for their tracks: add All Tracks
    struct slRef *allTracks = sortedAllTracks(fullTrackList);
    (void)writeGroupedTrack(jw, "allTracks", "All Tracks", fieldHash, excludeTypesHash,
                            maxDepth, allTracks);
    }
jsonWriteListEnd(jw);
jsonWriteObjectEnd(jw);
}
static struct jsonWrite *rTdbToJw(struct trackDb *tdb, struct hash *fieldHash,
                                  struct hash *excludeTypesHash, int depth, int maxDepth)
/* Recursively build and return a new jsonWrite object with JSON for tdb and its children,
 * or NULL if tdb or all children have been filtered out by excludeTypesHash.
 * If excludeTypesHash is non-NULL, omit any tracks/views/subtracks with type in excludeTypesHash.
 * If fieldHash is non-NULL, include only the field names indexed in fieldHash. */
{
if (maxDepth >= 0 && depth > maxDepth)
    return NULL;
boolean doSubtracks = (tdb->subtracks && fieldOk("subtracks", fieldHash));
// If excludeTypesHash is given and tdb is a leaf track/subtrack, look up the first word
// of tdb->type in excludeTypesHash; if found, return NULL.
if (excludeTypesHash && !doSubtracks)
    {
    char typeCopy[PATH_LEN];
    safecpy(typeCopy, sizeof(typeCopy), tdb->type);
    if (hashLookup(excludeTypesHash, firstWordInLine(typeCopy)))
        return NULL;
    }
boolean gotSomething = !doSubtracks;
struct jsonWrite *jwNew = jsonWriteNew();
jsonWriteObjectStart(jwNew, NULL);
writeTdbSimple(jwNew, tdb, fieldHash);
if (tdb->parent && fieldOk("parent", fieldHash))
    {
    // We can't link to an object in JSON and better not recurse here or else infinite loop.
    if (tdbIsSuperTrackChild(tdb))
        {
        // Supertracks have been omitted from fullTrackList, so add the supertrack object's
        // non-parent/child info here.
        jsonWriteObjectStart(jwNew, "parent");
        writeTdbSimple(jwNew, tdb->parent, fieldHash);
        jsonWriteObjectEnd(jwNew);
        }
    else
        // Just the name so we don't have infinite loops.
        jsonWriteString(jwNew, "parent", tdb->parent->track);
    }
if (doSubtracks)
    {
    jsonWriteListStart(jwNew, "subtracks");
    slSort(&tdb->subtracks, trackDbViewCmp);
    struct trackDb *subTdb;
    for (subTdb = tdb->subtracks;  subTdb != NULL;  subTdb = subTdb->next)
        {
        struct jsonWrite *jwSub = rTdbToJw(subTdb, fieldHash, excludeTypesHash, depth+1, maxDepth);
        if (jwSub)
            {
            gotSomething = TRUE;
            jsonWriteAppend(jwNew, NULL, jwSub);
            jsonWriteFree(&jwSub);
            }
        }
    jsonWriteListEnd(jwNew);
    }
jsonWriteObjectEnd(jwNew);
if (! gotSomething)
    // All children were excluded; clean up and null out jwNew.
    jsonWriteFree(&jwNew);
return jwNew;
}
Beispiel #16
0
struct jsonWrite *jsonForStep(struct sqlConnection *conn, struct eapStep *step)
/* Convert an eapStep to json. See step.json in same directory as the .c file. 
 * for an example. */
{
struct jsonWrite *jw = jsonWriteNew();
jsonWriteObjectStart(jw);

/* Write name and description. */
jsonWriteString(jw, "name", step->name);
jsonWriteString(jw, "description", step->description);

/* Write version */
struct eapStepVersion *ver = eapStepVersionLatest(conn, step->name);
jsonWriteNumber(jw, "version", ver->version);

/* Write software */
jsonWriteListStart(jw, "software");
char query[512];
sqlSafef(query, sizeof(query), "select * from eapStepSoftware where step='%s'", step->name);
struct eapStepSoftware *ss, *ssList = eapStepSoftwareLoadByQuery(conn, query);
boolean isFirst = TRUE;
struct dyString *dy = jw->dy;
for (ss = ssList; ss != NULL; ss = ss->next)
    {
    struct eapSoftware *software = eapSoftwareLoadByName(conn, ss->software);
    assert(software != NULL);
    if (software->metaUuid)
        {
	if (!isFirst)
	    {
	    dyStringAppendC(dy, ',');
	    dyStringAppendC(dy, '\n');
	    }
	isFirst = FALSE;
	dyStringAppendC(dy, '"');
	dyStringPrintf(dy, "/software/%s/", software->metaUuid);
	dyStringAppendC(dy, '"');
	}
    }
if (!isFirst)
    dyStringAppendC(dy, '\n');
jsonWriteListEnd(jw);
/* Done with writing software list */


/* Write input list */
jsonWriteListStart(jw, "inputs");
int i;
for (i=0; i<step->inCount; ++i)
    {
    jsonWriteObjectStart(jw);
    jsonWriteString(jw, "format", step->inputFormats[i]);
    jsonWriteString(jw, "name", step->inputTypes[i]);
    jsonWriteString(jw, "description", step->inputDescriptions[i]);
    jsonWriteObjectEnd(jw);
    }
jsonWriteListEnd(jw);

/* Write output list */
jsonWriteListStart(jw, "outputs");
for (i=0; i<step->outCount; ++i)
    {
    jsonWriteObjectStart(jw);
    jsonWriteString(jw, "format", step->outputFormats[i]);
    jsonWriteString(jw, "name", step->outputTypes[i]);
    jsonWriteString(jw, "description", step->outputDescriptions[i]);
    jsonWriteObjectEnd(jw);
    }
jsonWriteListEnd(jw);

jsonWriteObjectEnd(jw);
return jw;
}