package gov.nih.ncgc.bard.rest;
import gov.nih.ncgc.bard.entity.Assay;
import gov.nih.ncgc.bard.entity.BardLinkedEntity;
import gov.nih.ncgc.bard.entity.Biology;
import gov.nih.ncgc.bard.entity.Compound;
import gov.nih.ncgc.bard.entity.Experiment;
import gov.nih.ncgc.bard.entity.ExperimentData;
import gov.nih.ncgc.bard.entity.Project;
import gov.nih.ncgc.bard.entity.TargetClassification;
import gov.nih.ncgc.bard.search.Facet;
import gov.nih.ncgc.bard.tools.DBUtils;
import gov.nih.ncgc.bard.tools.OrderedSearchResultHandler;
import gov.nih.ncgc.bard.tools.Util;
import gov.nih.ncgc.search.MoleculeService;
import gov.nih.ncgc.search.SearchParams;
import gov.nih.ncgc.search.SearchService2;
import gov.nih.ncgc.util.MolRenderer;
import gov.nih.ncgc.util.functional.Functional;
import gov.nih.ncgc.util.functional.IApplyFunction;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.imageio.ImageIO;
import javax.ws.rs.Consumes;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.NotFoundException;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.EntityTag;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import chemaxon.struc.Molecule;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
/**
* Prototype of MLBD REST resources.
* <p/>
* This is mainly to explore the use of Jersey for presenting REST
* services for the MLBD
*
* @author Rajarshi Guha
*/
@Path("/compounds")
public class BARDCompoundResource extends BARDResource<Compound> {
public static final String DATE_FORMAT_NOW = "yyyy-MM-dd HH:mm:ss";
static final String VERSION = "1.0";
/**
* Normally using newCachedThreadPool is not recommended since it's
* an unbounded pool. However, given that our search handler is really
* meant to be short-lived, it's an ideal candidate for this particular
* thread pool. If this isn't the case, then a more refined instance
* of ThreadPoolExecutor should be used instead!
*/
static final ExecutorService threadPool = Executors.newCachedThreadPool();
/**
* these parameters should be configurable somewhere; having a static
* is really a bad idea here. We should hook this into the lifecycle
* of the web container so as to properly performing clean up!
*/
/*
static final ExecutorService threadPool =
new ThreadPoolExecutor (10, // core size
50, // max pool size
60, // idle timeout 1m
TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(20));
*/
public Class<Compound> getEntityClass() {
return Compound.class;
}
public String getResourceBase() {
return BARDConstants.API_BASE + "/compounds";
}
@GET
@Produces("text/plain")
@Path("/_info")
public String info() {
StringBuilder msg = new StringBuilder("Returns compound information\n\nAvailable resources:\n");
List<String> paths = Util.getResourcePaths(this.getClass());
for (String path : paths) msg.append(path).append("\n");
return msg.toString();
}
public Response getResources(@QueryParam("filter") String filter,
@QueryParam("expand") String expand,
@QueryParam("skip") Integer skip,
@QueryParam("top") Integer top) {
return getResources(null, filter, expand);
}
@GET
public Response getAll(@QueryParam("filter") String filter,
@QueryParam("expand") String expand,
@QueryParam("skip") Integer skip,
@QueryParam("top") Integer top,
@QueryParam("type") String type,
@QueryParam("cutoff") Double cutoff,
@QueryParam("rankBy") String rankBy,
@QueryParam("annot") String annot) throws SQLException, IOException {
Response response = null;
Long start, end;
if (skip == null) skip = -1;
if (top == null) top = -1;
boolean expandEntries = false;
if (expand != null && (expand.toLowerCase().equals("true") || expand.toLowerCase().equals("yes")))
expandEntries = true;
if (filter == null) {
if (countRequested)
response = Response.ok(String.valueOf(db.getEntityCount(Compound.class))).build();
else {
if ((top == -1)) { // top was not specified, so we start from the beginning
top = getDefaultEntityCount();
}
if (skip == -1) skip = 0;
String expandClause = "expand=false";
if (expandEntries) expandClause = "expand=true";
String linkString = null;
start = System.currentTimeMillis();
if (skip + top <= db.getEntityCount(Compound.class))
linkString = BARDConstants.API_BASE + "/compounds?skip=" + (skip + top) + "&top=" + top + "&" + expandClause;
end = System.currentTimeMillis();
System.out.println("TIME entity count: " + ((end - start) * 1e-3));
start = System.currentTimeMillis();
List<Compound> compounds = db.searchForEntity(filter, skip, top, Compound.class);
end = System.currentTimeMillis();
System.out.println("TIME entity search: " + ((end - start) * 1e-3));
if (expandEntries) {
BardLinkedEntity linkedEntity = new BardLinkedEntity(compounds, linkString);
start = System.currentTimeMillis();
response = Response.ok(Util.toJson(linkedEntity), MediaType.APPLICATION_JSON).build();
end = System.currentTimeMillis();
System.out.println("TIME json generate: " + ((end - start) * 1e-3));
} else {
List<String> links = new ArrayList<String>();
for (Compound a : compounds) links.add(a.getResourcePath());
BardLinkedEntity linkedEntity = new BardLinkedEntity(links, linkString);
response = Response.ok(Util.toJson(linkedEntity), MediaType.APPLICATION_JSON).build();
}
}
} else { // do a filtered search
// examine the filter argument to see if we should do a structure search
if (filter.contains("[structure]")) {
filter = filter.trim().replace("[structure]", "");
response = doStructureSearch(filter, type, top, skip, cutoff, rankBy, db, expandEntries, annot);
} else if (filter.contains("[tested]") || filter.contains("[active]")) {
if (countRequested && filter.contains("[tested]"))
response = Response.ok(String.valueOf(db.getCompoundTestCount())).build();
else if (countRequested && filter.contains("[active]"))
response = Response.ok(String.valueOf(db.getCompoundActiveCount())).build();
else {
if ((top == -1)) { // top was not specified, so we start from the beginning
top = getDefaultEntityCount();
}
if (skip == -1) skip = 0;
String expandClause = "expand=false";
if (expandEntries) expandClause = "expand=true";
String annotString = "annot=false";
if (annotString != null) annotString = "annot=" + annot;
String linkString = null;
if (skip + top <= db.getCompoundTestCount())
linkString = BARDConstants.API_BASE + "/compounds?skip=" + (skip + top) + "&top=" + top + "&" + expandClause + "&" + annotString + "&filter=[tested]";
List<Compound> compounds = db.searchForCompounds(filter, skip, top, annot != null && annot.toLowerCase().equals("true"));
if (expandEntries) {
BardLinkedEntity linkedEntity = new BardLinkedEntity(compounds, linkString);
response = Response.ok(Util.toJson(linkedEntity), MediaType.APPLICATION_JSON).build();
} else {
List<String> links = new ArrayList<String>();
for (Compound a : compounds) links.add(a.getResourcePath());
BardLinkedEntity linkedEntity = new BardLinkedEntity(links, linkString);
response = Response.ok(Util.toJson(linkedEntity), MediaType.APPLICATION_JSON).build();
}
}
}
}
db.closeConnection();
return response;
}
protected Response doStructureSearch(String query, String type, int top, int skip, Double cutoff, String rankBy,
DBUtils db, boolean expandEntries, String annot) throws SQLException, IOException {
return doStructureSearch(query, type, top, skip, cutoff, rankBy, db, expandEntries, annot, false);
}
protected Response doStructureSearch(String query, String type, int top, int skip, Double cutoff, String rankBy,
DBUtils db, boolean expandEntries, String annot, boolean probeSubset) throws SQLException, IOException {
Response response;
SearchService2 search = null;
try {
search = Util.getSearchService();
} catch (Exception e) {
throw new WebApplicationException(new Exception("Error in getting a search service instance", e), 500);
}
SearchParams params;
if (type != null) {
if (type.startsWith("sub")) {
params = SearchParams.substructure();
} else if (type.startsWith("super")) {
params = SearchParams.superstructure();
} else if (type.startsWith("sim")) {
params = SearchParams.similarity();
if (cutoff != null) {
try {
params.setSimilarity(cutoff);
} catch (NumberFormatException e) {
throw new BadRequestException("Bogus similarity value specified");
}
} else
throw new BadRequestException("If similarity search is requested must specify the cutoff");
} else if (type.startsWith("exact")) {
params = SearchParams.exact();
} else {
params = SearchParams.substructure();
}
} else {
params = SearchParams.substructure();
}
if (rankBy != null) params.setRankBy(rankBy);
System.out.println("## structure search: query:"
+ query + " type:" + type + " rank:" + rankBy);
StringWriter writer = new StringWriter();
PrintWriter pw = new PrintWriter(writer);
if (skip == -1) skip = 0;
if (top == -1) top = 100;
OrderedSearchResultHandler handler =
new OrderedSearchResultHandler(params, pw, skip, top);
if (countRequested) {
int n = search.count(query, params);
response = Response.ok(String.valueOf(n)).build();
} else {
handler.start(threadPool);
search.search(query, params, handler);
handler.complete();
// TODO we should be directly getting a List of cid's rather than parsing a string
String cidsStr = writer.getBuffer().toString();
String[] cidStrs = cidsStr.split("\n");
List<Long> cids = new ArrayList<Long>();
Map<Long, String> highlights = new HashMap<Long, String>();
for (String cidstr : cidStrs) {
String[] toks = cidstr.split("\t");
String hl = null;
if (toks.length == 2) {
cidstr = toks[0];
hl = toks[1];
}
if (cidstr.equals("")) continue;
Long cid = Long.parseLong(cidstr);
// TODO there should be a faster way to search only within the probe subset
if (probeSubset) {
Compound c = db.getCompoundsByCid(cid).get(0);
if (c.getProbeId() == null) continue;
}
// see if we should keep the cid, based on absence/presence of annotations
if (annot != null && annot.toLowerCase().equals("true")) {
if (db.getCompoundAnnotations(cid).get("anno_key").length > 0) cids.add(cid);
} else cids.add(cid);
if (hl != null) {
highlights.put(cid, hl);
}
}
Long[] ids = cids.toArray(new Long[0]);
EntityTag etag = newETag(db, ids);
// List<Long> cids = handler.getCids();
if (expandEntries) {
List<Compound> cs = db.getCompoundsByCid(ids);
for (Compound c : cs) {
String hl = highlights.get(c.getCid());
c.setHighlight(hl);
}
response = Response.ok(Util.toJson(cs), MediaType.APPLICATION_JSON).tag(etag).build();
} else {
List<String> paths = new ArrayList<String>();
for (Long cid : cids) {
Compound c = new Compound();
c.setCid(cid);
paths.add(c.getResourcePath());
}
String json = Util.toJson(paths);
response = Response.ok(json, MediaType.APPLICATION_JSON).header("content-length", json.length()).tag(etag).build();
}
}
return response;
}
private Response getCompoundResponse(String id, String type, List<MediaType> mediaTypes, boolean expand) throws SQLException, IOException {
List<String> validTypes = Arrays.asList("cid", "sid", "probeid", "name");
boolean isIdList = id.contains(",");
if (!validTypes.contains(type)) return null;
List<Compound> c = new ArrayList<Compound>();
try {
if (!isIdList) {
if (type.equals("cid") || type.equals("sid")) {
try {
Long.parseLong(id);
} catch (NumberFormatException e) {
throw new BadRequestException
("Invalid format for a CID or SID: " + id);
}
}
if (type.equals("cid")) c.addAll(db.getCompoundsByCid(Long.parseLong(id)));
else if (type.equals("probeid")) c.addAll(db.getCompoundsByProbeId(id));
else if (type.equals("sid")) c.addAll(db.getCompoundsBySid(Long.parseLong(id)));
else if (type.equals("name")) c.addAll(db.getCompoundsByName(id));
} else {
String[] s = id.split(",");
if (type.equals("cid") || type.equals("sid")) {
Long[] ids = new Long[s.length];
for (int i = 0; i < s.length; i++) ids[i] = Long.parseLong(s[i].trim());
if (type.equals("cid")) c.addAll(db.getCompoundsByCid(ids));
else if (type.equals("sid")) c.addAll(db.getCompoundsBySid(ids));
} else if (type.equals("probeid")) c.addAll(db.getCompoundsByProbeId(s));
else if (type.equals("name")) c.addAll(db.getCompoundsByProbeId(s));
}
if (c.size() == 0) throw new WebApplicationException(404);
if (countRequested) return Response.ok(String.valueOf(c.size()), MediaType.TEXT_PLAIN).build();
Long[] ids = new Long[c.size()];
for (int i = 0; i < c.size(); ++i) {
ids[i] = c.get(i).getCid();
}
EntityTag etag = newETag(db, ids);
if (mediaTypes.contains(BARDConstants.MIME_SMILES)) {
StringBuilder s = new StringBuilder();
for (Compound ac : c) s.append(ac.getSmiles() + "\t" + ac.getCid());
return Response.ok(s, BARDConstants.MIME_SMILES)
.tag(etag).build();
} else if (mediaTypes.contains(BARDConstants.MIME_SDF)) { // TODO handle multi-molecule SDFs
throw new WebApplicationException(406);
// Molecule mol = MolImporter.importMol(c.getSmiles());
// mol.setProperty("cid", String.valueOf(c.getCid()));
// mol.setProperty("probeId", c.getProbeId());
// mol.setProperty("url", c.getUrl());
// mol.setProperty("resourecePath", c.getResourcePath());
// String sdf = mol.exportToFormat("sdf");
// return Response.ok(sdf, BARDConstants.MIME_SDF).build();
} else {
String json;
ObjectMapper mapper = new ObjectMapper();
if (!type.equals("name") && c.size() == 1) {
if (type.equals("probeid")) {
List<Project> projects = db.getProjectByProbeId(c.get(0).getProbeId());
ArrayNode anode = mapper.createArrayNode();
ObjectNode onode = mapper.valueToTree(c.get(0));
if (projects.size() == 1) {
onode.put("bardProjectId", projects.get(0).getBardProjectId());
onode.put("capProjectId", projects.get(0).getCapProjectId());
} else if (projects.size() == 0) {
onode.put("bardProjectId", -1);
onode.put("capProjectId", -1);
}
anode.add(onode);
json = mapper.writeValueAsString(anode);
} else json = Util.toJson(c);
} else {
if (expand) {
json = toJson(db, c, type, false);
} else {
List<String> links = new ArrayList<String>();
for (Compound ac : c) links.add(ac.getResourcePath());
json = Util.toJson(links);
}
}
return Response.ok(json, MediaType.APPLICATION_JSON)
.tag(etag).build();
}
} finally {
db.closeConnection();
}
}
EntityTag newETag(DBUtils db, Long... ids) throws SQLException {
EntityTag etag = new EntityTag
(db.newETag(getRequestURI(), Compound.class.getName()));
int cnt = db.putETag(etag.getValue(), ids);
log("** ETag: " + etag.getValue() + " " + cnt);
List<String> parents = new ArrayList<String>();
for (EntityTag e : getETagsRequested()) {
cnt = db.putETag(e.getValue(), ids);
parents.add(e.getValue());
log(" ** Updating " + e.getValue() + ": " + cnt);
}
if (!parents.isEmpty()) {
db.createETagLinks(etag.getValue(),
parents.toArray(new String[0]));
}
return etag;
}
String toJson(DBUtils db, List<Compound> compounds, String type,
boolean annotation) throws SQLException, IOException {
if (!annotation && !type.equals("probeid")) {
return Util.toJson(compounds);
}
ObjectMapper mapper = new ObjectMapper();
ArrayNode node = (ArrayNode) mapper.valueToTree(compounds);
for (int i = 0; i < node.size(); ++i) {
ObjectNode n = (ObjectNode) node.get(i);
if (type.equals("probeid")) {
String probeId = node.get("probeId").asText();
List<Project> projects = db.getProjectByProbeId(probeId);
if (projects.size() == 1) {
if (annotation) n.put("bardProjectId", mapper.valueToTree(projects.get(0)));
else n.put("bardProjectId", projects.get(0).getBardProjectId());
} else logger.warning("There were " + projects.size() + " projects for probe " + probeId);
}
Map anno = db.getCompoundAnnotations(n.get("cid").asLong());
if (anno.isEmpty()) {
n.putNull("anno_key");
n.putNull("anno_val");
} else {
for (Object key : anno.entrySet()) {
Map.Entry me = (Map.Entry) key;
n.putPOJO((String) me.getKey(), me.getValue());
}
}
}
return mapper.writeValueAsString(node);
}
@GET
@Path("/{cid}/image")
public Response getImage(@PathParam("cid") String resourceId,
@QueryParam("s") Integer s,
@QueryParam("c") String c,
@QueryParam("a") String a) {
try {
MoleculeService molsrv = (MoleculeService) Util.getMoleculeService();
Molecule molecule = molsrv.getMol(resourceId);
if (molecule == null) throw new NotFoundException("No molecule for CID = " + resourceId);
MolRenderer renderer = new MolRenderer();
// size
int size = 120;
if (s != null && s >= 16 && s <= 512) size = s;
// atom
if (a != null) {
for (String idx : a.split(",")) {
try {
int i = Integer.parseInt(idx);
if (i > 0 && i <= molecule.getAtomCount()) {
molecule.getAtom(i - 1).setAtomMap(1);
}
} catch (NumberFormatException ex) {
throw new WebApplicationException(ex, 400);
}
}
}
if (c != null) {
try {
Color color = Color.decode(c);
renderer.setBackground(color);
} catch (NumberFormatException ex) {
throw new WebApplicationException(ex, 400);
}
}
BufferedImage img = renderer.createImage(molecule, size);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(img, "png", baos);
return Response.ok(baos.toByteArray()).type("image/png").build();
} catch (SQLException e) {
throw new WebApplicationException(e, 500);
} catch (IOException e) {
throw new WebApplicationException(e, 500);
} catch (Exception e) {
throw new WebApplicationException(e, 500);
}
}
@GET
@Path("/{cid}/{format}")
public Response getImage(@PathParam("cid") String resourceId,
@PathParam("format") String format) {
try {
MoleculeService molsrv =
(MoleculeService) Util.getMoleculeService();
Molecule molecule = molsrv.getMol(resourceId);
if (molecule == null)
throw new NotFoundException
("No molecule for CID = " + resourceId);
String molstr = "";
String type = "text/plain";
if (format.startsWith("mol")) {
molstr = molecule.toFormat("mol");
type = "chemical/x-mdl-mol";
} else if (format.startsWith("sdf")) {
molstr = molecule.toFormat("sdf");
type = "chemical/x-mdl-sdffile";
} else if (format.startsWith("smi")) {
molstr = molecule.toFormat("smiles:q");
type = "chemical/x-daylight-smiles";
}
return Response.ok(molstr).type(type).build();
} catch (Exception e) {
throw new WebApplicationException(e, 500);
}
}
@GET
@Path("/{hash}/h{id}")
public Response getCompoundsByHash(@PathParam("hash") String hv,
@PathParam("id") String id,
@QueryParam("expand") String expand) {
try {
if (id == null || id.length() == 0) {
throw new IllegalArgumentException("No hash id specified!");
}
if ((hv.length() % 6) != 0) {
// not multiple of 6
throw new IllegalArgumentException
("Hash value is not multiple of 6");
}
/*
* {hash} is the hash value which can be of length
* 6, 12, 18, or 24 depending on {id}.
* {id} must can be any combination of {1,2,3,4}; e.g.,
* /Gwdyar/h1
* /GwdyarAykhoO/h13
* /AykhoOGwdyar/h31
* /GwdyarvpREqoAykhoO/h123
* /GwdyarvpREqoAykhoOoTARYx/h1234
*/
String[] hash = new String[4];
for (int i = 0, pos = 0;
i < id.length() && pos < hv.length(); ++i) {
String key = hv.substring(pos, pos + 6);
switch (id.charAt(i)) {
case '1':
hash[0] = key;
break;
case '2':
hash[1] = key;
break;
case '3':
hash[2] = key;
break;
case '4':
hash[3] = key;
break;
default:
// ignore bogus character
}
pos += 6;
}
log("{hash} = " + hv + "; {id} = " + id + "; h1=" + hash[0] + " h2=" + hash[1]
+ " h3=" + hash[2] + " h4=" + hash[3]);
List<Compound> compounds =
db.getCompoundsByHash(hash[0], hash[1], hash[2], hash[3]);
Response response;
if (expand != null && (expand.equalsIgnoreCase("true")
|| expand.equalsIgnoreCase("yes"))) {
response = Response.ok(Util.toJson(compounds),
MediaType.APPLICATION_JSON).build();
} else {
List<String> links = new ArrayList<String>();
for (Compound a : compounds)
links.add(a.getResourcePath());
response = Response.ok(Util.toJson(links),
MediaType.APPLICATION_JSON).build();
}
return response;
} catch (Exception e) {
throw new WebApplicationException(e, 500);
} finally {
try {
db.closeConnection();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
@GET
@Path("/{cid}/eqv")
public Response getEqvCompounds(@PathParam("cid") String cid,
@QueryParam("expand") String expand) {
// return equivalence class based on hash keys
try {
List<Compound> compounds =
db.getEqvCompounds(Long.parseLong(cid));
Response response;
if (expand != null && (expand.equalsIgnoreCase("true")
|| expand.equalsIgnoreCase("yes"))) {
response = Response.ok(Util.toJson(compounds),
MediaType.APPLICATION_JSON).build();
} else {
List<String> links = new ArrayList<String>();
for (Compound a : compounds)
links.add(a.getResourcePath());
response = Response.ok(Util.toJson(links),
MediaType.APPLICATION_JSON).build();
}
return response;
} catch (Exception ex) {
throw new WebApplicationException(ex, 500);
} finally {
try {
db.closeConnection();
} catch (Exception ex) {
}
}
}
@GET
@Path("/{cid}")
public Response getResources(@PathParam("cid") String resourceId,
@QueryParam("filter") String filter,
@QueryParam("expand") String expand) {
try {
Response response = getCompoundResponse(resourceId, "cid", headers.getAcceptableMediaTypes(), expand != null && expand.toLowerCase().equals("true"));
return response;
} catch (SQLException e) {
throw new WebApplicationException(e, 500);
} catch (IOException e) {
throw new WebApplicationException(e, 500);
}
}
@POST
@Path("/")
@Consumes("application/x-www-form-urlencoded")
public Response getResources(@FormParam("ids") String cids, @QueryParam("expand") String expand) {
try {
if (cids == null)
throw new WebApplicationException(new Exception("POST request must specify the cids form parameter, which should be a comma separated string of CIDs"), 400);
Response response = getCompoundResponse(cids, "cid", headers.getAcceptableMediaTypes(), expand != null && expand.toLowerCase().equals("true"));
return response;
} catch (SQLException e) {
throw new WebApplicationException(e, 500);
} catch (IOException e) {
throw new WebApplicationException(e, 500);
}
}
@Override
@GET
@Path("/etag/{etag}")
public Response getEntitiesByETag(@PathParam("etag") String resourceId,
@QueryParam("filter") String filter,
@QueryParam("expand") String expand,
@QueryParam("skip") Integer skip,
@QueryParam("top") Integer top) {
try {
List<Compound> c = db.getCompoundsByETag
(skip != null ? skip : -1, top != null ? top : -1, resourceId);
String json = toJson(db, c, "", expand != null && expand.toLowerCase().equals("true"));
return Response.ok(json, MediaType.APPLICATION_JSON).build();
} catch (Exception e) {
throw new WebApplicationException(e, 500);
} finally {
try {
db.closeConnection();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
@GET
@Path("/etag/{etag}/assays")
public Response getAssaysByETag(@PathParam("etag") String resourceId,
@QueryParam("filter") String filter,
@QueryParam("expand") String expand,
@QueryParam("skip") Integer skip,
@QueryParam("top") Integer top) {
try {
List<Compound> c = db.getCompoundsByETag
(skip != null ? skip : -1, top != null ? top : -1, resourceId);
Map<Long, List> ret = new HashMap<Long, List>();
for (Compound ac : c) {
Long cid = ac.getCid();
List<Assay> p;
if (filter == null || !filter.trim().toLowerCase().equals("active"))
p = db.getEntitiesByCid(cid, Assay.class, 0, -1);
else p = db.getEntitiesByActiveCid(cid, Assay.class, 0, -1);
if (p == null) p = new ArrayList<Assay>();
if (expandEntries(expand)) ret.put(cid, p);
else {
List<String> links = Functional.Apply(p, new IApplyFunction<Assay, String>() {
public String eval(Assay assay) {
return assay.getResourcePath();
}
});
ret.put(cid, links);
}
}
String json = Util.toJson(ret);
return Response.ok(json, MediaType.APPLICATION_JSON).build();
} catch (Exception e) {
throw new WebApplicationException(e, 500);
} finally {
try {
db.closeConnection();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
@Override
@GET
@Path("/etag/{etag}/facets")
public Response getFacets(@PathParam("etag") String resourceId) {
try {
List<Facet> facets = db.getCompoundFacets(resourceId);
return Response.ok(Util.toJson(facets),
MediaType.APPLICATION_JSON).build();
} catch (Exception ex) {
throw new WebApplicationException(ex, 500);
} finally {
try {
db.closeConnection();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
@GET
@Path("/sid/{sid}")
public Response getCompoundBySid(@PathParam("sid") String resourceId, @QueryParam("filter") String filter, @QueryParam("expand") String expand) {
try {
Response response = getCompoundResponse(resourceId, "sid", headers.getAcceptableMediaTypes(), expand != null && expand.toLowerCase().equals("true"));
return response;
} catch (SQLException e) {
throw new WebApplicationException(e, 500);
} catch (IOException e) {
throw new WebApplicationException(e, 500);
}
}
@POST
@Path("/sid/")
@Consumes("application/x-www-form-urlencoded")
public Response getCompoundBySid(@FormParam("sids") String sids, @QueryParam("expand") String expand) {
try {
if (sids == null)
throw new WebApplicationException(new Exception("POST request must specify the sids form parameter, which should be a comma separated string of SIDs"), 400);
Response response = getCompoundResponse(sids, "sid", headers.getAcceptableMediaTypes(), expand != null && expand.toLowerCase().equals("true"));
return response;
} catch (SQLException e) {
throw new WebApplicationException(e, 500);
} catch (IOException e) {
throw new WebApplicationException(e, 500);
}
}
@GET
@Path("/probeid/{pid}")
public Response getCompoundByProbeid(@PathParam("pid") String resourceId, @QueryParam("filter") String filter, @QueryParam("expand") String expand) {
try {
Response response = getCompoundResponse(resourceId, "probeid", headers.getAcceptableMediaTypes(), expand != null && expand.toLowerCase().equals("true"));
return response;
} catch (SQLException e) {
e.printStackTrace();
throw new WebApplicationException(e, 500);
} catch (IOException e) {
e.printStackTrace();
throw new WebApplicationException(e, 500);
}
}
@POST
@Path("/probeid/{pid}")
@Consumes("application/x-www-form-urlencoded")
public Response getCompoundByProbeid(@FormParam("pids") String pids, @QueryParam("expand") String expand) {
try {
if (pids == null)
throw new WebApplicationException(new Exception("POST request must specify the pids form parameter, which should be a comma separated string of probe id's"), 400);
Response response = getCompoundResponse(pids, "probeid", headers.getAcceptableMediaTypes(), expand != null && expand.toLowerCase().equals("true"));
return response;
} catch (SQLException e) {
throw new WebApplicationException(e, 500);
} catch (IOException e) {
throw new WebApplicationException(e, 500);
}
}
@GET
@Path("/name/{name}")
public Response getCompoundByName(@PathParam("name") String name, @QueryParam("filter") String filter, @QueryParam("expand") String expand) {
try {
Response response = getCompoundResponse(name, "name", headers.getAcceptableMediaTypes(), expand != null && expand.toLowerCase().equals("true"));
return response;
} catch (SQLException e) {
throw new WebApplicationException(e, 500);
} catch (IOException e) {
throw new WebApplicationException(e, 500);
}
}
@POST
@Path("/name/")
@Consumes("application/x-www-form-urlencoded")
public Response getCompoundByNameList(@FormParam("names") String names, @QueryParam("expand") String expand) {
if (names == null || names.trim().equals("")) throw new WebApplicationException(400);
String[] toks = names.trim().split(",");
Map<String, List<Compound>> map = new HashMap<String, List<Compound>>();
Response response = null;
try {
for (String tok : toks) map.put(tok.trim(), db.getCompoundsByName(tok.trim()));
db.closeConnection();
if (expandEntries(expand)) response = Response.ok(Util.toJson(map), MediaType.APPLICATION_JSON).build();
else {
Map<String, List<String>> lmap = new HashMap<String, List<String>>();
for (String key : map.keySet()) {
List<Compound> compounds = map.get(key);
List<String> links = new ArrayList<String>();
for (Compound c : compounds) links.add(c.getResourcePath());
lmap.put(key, links);
}
response = Response.ok(Util.toJson(lmap), MediaType.APPLICATION_JSON).build();
}
} catch (SQLException e) {
throw new WebApplicationException(e, 500);
} catch (IOException e) {
throw new WebApplicationException(e, 500);
}
return response;
}
@GET
@Path("/{cid}/projects")
public Response getProjectsForCompound(@PathParam("cid") Long cid,
@QueryParam("expand") String expand,
@QueryParam("skip") Integer skip,
@QueryParam("top") Integer top) throws SQLException, IOException {
Response response;
String linkString = null;
if (top == null || top == -1) { // top was not specified, so we start from the beginning
top = BARDConstants.MAX_DATA_COUNT;
}
if (skip == null || skip == -1) skip = 0;
List<Project> p = db.getEntitiesByCid(cid, Project.class, skip, top);
if (countRequested) response = Response.ok(String.valueOf(p.size())).type(MediaType.TEXT_PLAIN).build();
if (p.size() > BARDConstants.MAX_DATA_COUNT) {
String expandClause = "expand=false";
if (expandEntries(expand)) expandClause = "expand=true";
if (skip + top <= p.size())
linkString = BARDConstants.API_BASE + "/compounds/" + cid + "/projects?skip=" + (skip + top) + "&top=" + top + "&" + expandClause;
}
if (!expandEntries(expand)) {
List<String> links = Functional.Apply(p, new IApplyFunction<Project, String>() {
public String eval(Project project) {
return project.getResourcePath();
}
});
BardLinkedEntity linkedEntity = new BardLinkedEntity(links, linkString);
response = Response.ok(Util.toJson(linkedEntity)).type(MediaType.APPLICATION_JSON).build();
} else {
BardLinkedEntity linkedEntity = new BardLinkedEntity(p, linkString);
response = Response.ok(Util.toJson(linkedEntity)).type(MediaType.APPLICATION_JSON).build();
}
db.closeConnection();
return response;
}
@GET
@Path("/{cid}/annotations")
public Response getCompoundAnnotations(@PathParam("cid") Long cid)
throws SQLException, IOException {
try {
Map anno = db.getCompoundAnnotations(cid);
return Response.ok(Util.toJson(anno))
.type(MediaType.APPLICATION_JSON).build();
} finally {
db.closeConnection();
}
}
@GET
@Path("/{cid}/sids")
public Response getSidsForCompound(@PathParam("cid") Long cid)
throws SQLException, IOException {
try {
List<Long> sids = db.getSidsByCid(cid);
return Response.ok(Util.toJson(sids))
.type(MediaType.APPLICATION_JSON).build();
} finally {
db.closeConnection();
}
}
@GET
@Path("/{cid}/synonyms")
public Response getSynonymsForCompound(@PathParam("cid") Long cid)
throws SQLException, IOException {
try {
List<String> syns = db.getCompoundSynonyms(cid);
return Response.ok(Util.toJson(syns))
.type(MediaType.APPLICATION_JSON).build();
} finally {
db.closeConnection();
}
}
@POST
@Path("/assays")
@Consumes("application/x-www-form-urlencoded")
public Response getAssaysForCompounds(@FormParam("ids") String ids,
@QueryParam("expand") String expand,
@QueryParam("filter") String filter,
@QueryParam("skip") Integer skip,
@QueryParam("top") Integer top) throws SQLException, IOException {
Response response;
String linkString = null;
String[] toks = ids.split(",");
Long[] cids = new Long[toks.length];
for (int i = 0; i < toks.length; i++) cids[i] = Long.parseLong(toks[i].trim());
Map<Long, List> ret = new HashMap<Long, List>();
for (Long cid : cids) {
List<Assay> p;
if (filter == null || !filter.trim().toLowerCase().equals("active"))
p = db.getEntitiesByCid(cid, Assay.class, skip, top);
else p = db.getEntitiesByActiveCid(cid, Assay.class, skip, top);
if (p == null) p = new ArrayList<Assay>();
if (expandEntries(expand)) ret.put(cid, p);
else {
List<String> links = Functional.Apply(p, new IApplyFunction<Assay, String>() {
public String eval(Assay assay) {
return assay.getResourcePath();
}
});
ret.put(cid, links);
}
}
response = Response.ok(Util.toJson(ret)).type(MediaType.APPLICATION_JSON).build();
db.closeConnection();
return response;
}
@GET
@Path("/{cid}/assays")
public Response getAssaysForCompound(@PathParam("cid") Long cid,
@QueryParam("expand") String expand,
@QueryParam("filter") String filter,
@QueryParam("skip") Integer skip,
@QueryParam("top") Integer top) throws SQLException, IOException {
Response response;
String linkString = null;
List<Assay> p;
if (filter == null || !filter.trim().toLowerCase().equals("active"))
p = db.getEntitiesByCid(cid, Assay.class, skip, top);
else p = db.getEntitiesByActiveCid(cid, Assay.class, skip, top);
if (countRequested) return Response.ok(String.valueOf(p.size())).type(MediaType.TEXT_PLAIN).build();
if (top == null) top = -1;
if (skip == null) skip = -1;
if (p.size() > BARDConstants.MAX_DATA_COUNT) {
if (top == -1) { // top was not specified, so we start from the beginning
top = BARDConstants.MAX_DATA_COUNT;
}
if (skip == -1) skip = 0;
String expandClause = "expand=false";
if (expandEntries(expand)) expandClause = "expand=true";
if (skip + top <= p.size())
linkString = BARDConstants.API_BASE + "/compounds/" + cid + "/assays?skip=" + (skip + top) + "&top=" + top + "&" + expandClause;
}
if (!expandEntries(expand)) {
List<String> links = Functional.Apply(p, new IApplyFunction<Assay, String>() {
public String eval(Assay assay) {
return assay.getResourcePath();
}
});
BardLinkedEntity linkedEntity = new BardLinkedEntity(links, linkString);
response = Response.ok(Util.toJson(linkedEntity)).type(MediaType.APPLICATION_JSON).build();
} else {
BardLinkedEntity linkedEntity = new BardLinkedEntity(p, linkString);
response = Response.ok(Util.toJson(linkedEntity)).type(MediaType.APPLICATION_JSON).build();
}
db.closeConnection();
return response;
}
// return all experiment data for this CID
@GET
@Path("/{cid}/exptdata")
public Response getExperimentData(@PathParam("cid") String resourceId,
@QueryParam("filter") String filter,
@QueryParam("expand") String expand,
@QueryParam("skip") Integer skip,
@QueryParam("top") Integer top) {
String linkString = null;
if (skip == null) skip = -1;
if (top == null) top = -1;
try {
List<String> edids = db.getCompoundDataIds(Long.valueOf(resourceId), skip, top, filter);
// // set up skip and top params
// if (experiemnt.getSubstances() > BARDConstants.MAX_DATA_COUNT) {
// if ((top == -1)) { // top was not specified, so we start from the beginning
// top = BARDConstants.MAX_DATA_COUNT;
// }
// if (skip == -1) skip = 0;
// String expandClause = "expand=false";
// if (expandEntries(expand)) expandClause = "expand=true";
// if (skip + top <= experiemnt.getSubstances())
// linkString = BARDConstants.API_BASE + "/compounds/" + resourceId + "/exptdata?skip=" + (skip + top) + "&top=" + top + "&" + expandClause;
// }
String json;
if (!expandEntries(expand)) {
if (countRequested) json = String.valueOf(edids.size());
else {
List<String> links = Functional.Apply(edids, new IApplyFunction<String, String>() {
public String eval(String aString) {
ExperimentData ed = new ExperimentData();
ed.setExptDataId(aString);
return ed.getResourcePath();
}
});
BardLinkedEntity linkedEntity = new BardLinkedEntity(links, linkString);
json = Util.toJson(linkedEntity);
}
} else {
List<ExperimentData> data = db.getExperimentDataByDataId(edids);
if (countRequested) json = String.valueOf(data.size());
else {
BardLinkedEntity linkedEntity = new BardLinkedEntity(data, linkString);
json = Util.toJson(linkedEntity);
}
}
db.closeConnection();
return Response.ok(json, MediaType.APPLICATION_JSON).build();
} catch (SQLException e) {
throw new WebApplicationException(e, 500);
} catch (IOException e) {
throw new WebApplicationException(e, 500);
}
}
@GET
@Path("/{cid}/experiments")
public Response getExperiments(@PathParam("cid") String resourceId,
@QueryParam("filter") String filter,
@QueryParam("expand") String expand,
@QueryParam("skip") Integer skip,
@QueryParam("top") Integer top) {
String linkString = null;
if (skip == null) skip = -1;
if (top == null) top = -1;
try {
// set up skip and top params
if ((top == -1)) { // top was not specified, so we start from the beginning
top = BARDConstants.MAX_DATA_COUNT;
}
if (skip == -1) skip = 0;
String expandClause = "expand=false";
if (expandEntries(expand)) expandClause = "expand=true";
linkString = BARDConstants.API_BASE + "/compounds/" + resourceId + "/experiments?skip=" + (skip + top) + "&top=" + top + "&" + expandClause;
String json;
List<Experiment> data;
if (filter == null || !filter.trim().toLowerCase().equals("active"))
data = db.getEntitiesByCid(Long.valueOf(resourceId), Experiment.class, skip, top);
else data = db.getEntitiesByActiveCid(Long.valueOf(resourceId), Experiment.class, skip, top);
if (!expandEntries(expand)) {
List<Long> eids = new ArrayList<Long>();
for (Experiment expt : data) eids.add(expt.getBardExptId());
if (countRequested) json = String.valueOf(eids.size());
else {
List<String> links = new ArrayList<String>();
for (Long eid : eids) {
Experiment ed = new Experiment();
ed.setBardExptId(eid);
links.add(ed.getResourcePath());
}
BardLinkedEntity linkedEntity = new BardLinkedEntity(links, linkString);
json = Util.toJson(linkedEntity);
}
} else {
if (countRequested) json = String.valueOf(data.size());
else {
BardLinkedEntity linkedEntity = new BardLinkedEntity(data, linkString);
json = Util.toJson(linkedEntity);
}
}
db.closeConnection();
return Response.ok(json, MediaType.APPLICATION_JSON).build();
} catch (SQLException e) {
throw new WebApplicationException(e, 500);
} catch (IOException e) {
throw new WebApplicationException(e, 500);
}
}
private Map<String, Integer> getTargetClassCount(List<String> acc, int level) throws SQLException {
List<String> tclasses = new ArrayList<String>(); //db.getChemblTargetClasses(acc, level);
for (String anAcc : acc) {
List<TargetClassification> pclasses = db.getPantherClassesForAccession(anAcc);
for (TargetClassification pclass : pclasses) tclasses.add(pclass.getName());
}
Map<String, Integer> map = new HashMap<String, Integer>();
for (String tclass : tclasses) {
if (tclass == null) continue;
if (map.containsKey(tclass)) map.put(tclass, map.get(tclass) + 1);
else map.put(tclass, 0);
}
db.closeConnection();
return map;
}
@GET
@Path("/{cid}/summary")
public Response getSummary(@PathParam("cid") Long cid,
@QueryParam("expand") String expand,
@QueryParam("skip") Integer skip,
@QueryParam("top") Integer top) throws IOException, SQLException {
Map<String, Object> s = new HashMap<String, Object>();
if (skip == null) skip = -1;
if (skip == null) skip = -1;
if (top == null) top = -1;
List<ExperimentData> data = db.getEntitiesByCid(cid, ExperimentData.class, skip, top);
// make an array of experiment ids then pull all expts
// in bulk and set up local cache
List<Long> tmpEids = new ArrayList<Long>();
for (ExperimentData edata : data) {
if (edata != null) tmpEids.add(edata.getBardExptId());
}
List<Experiment> experiments = db.getExperimentsByExptIds(tmpEids.toArray(new Long[0]));
Map<Long, Experiment> localExptCache = new HashMap<Long, Experiment>();
for (Experiment expt : experiments) localExptCache.put(expt.getBardExptId(), expt);
// do the same thing for the assays
List<Long> tmpAids = new ArrayList<Long>();
for (Experiment expt : experiments) {
if (expt.getBardAssayId() != null) tmpAids.add(expt.getBardAssayId());
}
List<Assay> assays = db.getAssays(tmpAids.toArray(new Long[0]));
Map<Long, Assay> localAssayCache = new HashMap<Long, Assay>();
for (Assay assay : assays) localAssayCache.put(assay.getBardAssayId(), assay);
List<ExperimentData> hitData = new ArrayList<ExperimentData>();
int nhit = 0;
List<String> hitExpts = new ArrayList<String>();
Set<Assay> hitAssays = new HashSet<Assay>();
Set<Assay> testedAssays = new HashSet<Assay>();
List<Experiment> testedExperiments = new ArrayList<Experiment>();
for (ExperimentData ed : data) {
if (ed == null) {
logger.warning("Should not have a null ExperimentData object for compound " + cid + ". Skipping");
continue;
}
Long eid = ed.getBardExptId();
Experiment expt = localExptCache.get(eid);
if (expt == null) continue;
Long aid = expt.getBardAssayId();
testedExperiments.add(expt);
if (aid != null) {
Assay assay = localAssayCache.get(aid);
testedAssays.add(assay);
// if cid was active in experiment_data (outcome = 2) and experiment was a confirmatory screen, we call it a hit
if (ed.getOutcome() == 2) {
nhit++;
hitExpts.add(expt.getResourcePath());
hitAssays.add(assay);
hitData.add(ed);
}
} else {
logger.warning("Something is rotten in the state of Denmark! "
+ "No assay found for eid=" + eid + " exptid="
+ ed.getExptDataId() + " bardexptid="
+ ed.getBardExptId() + "!");
}
}
s.put("ntest", data.size());
s.put("nhit", nhit);
// s.put("hitExperiments", hitExpts);
s.put("hitAssays", hitAssays);
s.put("testedExptdata", data);
s.put("hitExptdata", hitData);
// get target class counts
List<String> accs = new ArrayList<String>();
for (Assay a : testedAssays) {
List<Biology> bios = db.getBiologyByEntity("assay", a.getBardAssayId());
for (Biology bio : bios) {
if (bio.getBiology().equals(Biology.BiologyType.PROTEIN) && bio.getDictId() == 1398) // Uniprot ID
accs.add(bio.getExtId());
}
}
if (accs.size() > 0)
s.put("testedTargets", accs);
else s.put("testedTarget", null);
accs = new ArrayList<String>();
for (Assay a : hitAssays) {
List<Biology> bios = db.getBiologyByEntity("assay", a.getBardAssayId());
for (Biology bio : bios) {
if (bio.getBiology().equals(Biology.BiologyType.PROTEIN) && bio.getDictId() == 1398) // Uniprot ID
accs.add(bio.getExtId());
}
}
if (accs.size() > 0)
s.put("hitTarget", accs);
else s.put("hitTarget", null);
if (expand != null && expand.trim().toLowerCase().equals("true")) {
// s.put("testedExperiments", testedExperiments);
s.put("testedAssays", testedAssays);
s.put("hitAssays", hitAssays);
} else {
List<String> l = new ArrayList<String>();
for (Experiment e : testedExperiments) {
if (e.getBardExptId() != null)
l.add(e.getResourcePath());
}
// s.put("testedExperiments", l);
l = new ArrayList<String>();
for (Assay a : testedAssays) {
if (a != null) l.add(a.getResourcePath());
else logger.warning("Should not have a null assay for compound " + cid + ". Skipping");
}
s.put("testedAssays", l);
l = new ArrayList<String>();
for (Assay a : hitAssays) {
if (a != null) l.add(a.getResourcePath());
else logger.warning("Should not have a null assay for compound " + cid + ". Skipping");
}
s.put("hitAssays", l);
}
db.closeConnection();
return Response.ok(Util.toJson(s), MediaType.APPLICATION_JSON).build();
}
}