Skip to content

Commit

Permalink
save to file
Browse files Browse the repository at this point in the history
  • Loading branch information
Luke Sikina committed Nov 10, 2023
1 parent 47b827b commit ca8b278
Show file tree
Hide file tree
Showing 6 changed files with 219 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.core.Response.Status;

import edu.harvard.hms.dbmi.avillach.hpds.service.filesharing.FileSharingService;
import edu.harvard.hms.dbmi.avillach.hpds.service.filesharing.FileSystemService;
import edu.harvard.hms.dbmi.avillach.hpds.service.util.Paginator;
import org.apache.http.entity.ContentType;
import org.slf4j.Logger;
Expand All @@ -34,6 +36,7 @@
import edu.harvard.hms.dbmi.avillach.hpds.data.query.Query;
import edu.harvard.hms.dbmi.avillach.hpds.processing.*;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestBody;

@Path("PIC-SURE")
@Produces("application/json")
Expand All @@ -42,13 +45,16 @@ public class PicSureService implements IResourceRS {

@Autowired
public PicSureService(QueryService queryService, TimelineProcessor timelineProcessor, CountProcessor countProcessor,
VariantListProcessor variantListProcessor, AbstractProcessor abstractProcessor, Paginator paginator) {
VariantListProcessor variantListProcessor, AbstractProcessor abstractProcessor,
Paginator paginator, FileSharingService fileSystemService
) {
this.queryService = queryService;
this.timelineProcessor = timelineProcessor;
this.countProcessor = countProcessor;
this.variantListProcessor = variantListProcessor;
this.abstractProcessor = abstractProcessor;
this.paginator = paginator;
this.fileSystemService = fileSystemService;
Crypto.loadDefaultKey();
}

Expand All @@ -68,6 +74,8 @@ public PicSureService(QueryService queryService, TimelineProcessor timelineProce

private final Paginator paginator;

private final FileSharingService fileSystemService;

private static final String QUERY_METADATA_FIELD = "queryMetadata";
private static final int RESPONSE_CACHE_SIZE = 50;

Expand Down Expand Up @@ -250,6 +258,36 @@ public Response queryResult(@PathParam("resourceQueryId") UUID queryId, QueryReq
}
}

@GET
@Path("/write/{dataType}")
public Response writeQueryResult(
@RequestBody() Query query, @PathParam("dataType") String datatype
) {
AsyncResult result = queryService.getResultFor(query.toString());
// the queryResult has this DIY retry logic that blocks a system thread.
// I'm not going to do that here. If the service can't find it, you get a 404.
// Retry it client side.
if (result == null) {
return Response.status(404).build();
}
if (result.status == AsyncResult.Status.ERROR) {
return Response.status(500).build();
}
if (result.status != AsyncResult.Status.SUCCESS) {
return Response.status(503).build(); // 503 = unavailable
}

// at least for now, this is going to block until we finish writing
// Not very restful, but it will make this API very easy to consume
boolean success = false;
if ("phenotypic".equals(datatype)) {
success = fileSystemService.createPhenotypicData(query);
} else if ("genomic".equals(datatype)) {
success = fileSystemService.createGenomicData(query);
}
return success ? Response.ok().build() : Response.serverError().build();
}

@POST
@Path("/query/{resourceQueryId}/status")
@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package edu.harvard.hms.dbmi.avillach.hpds.service.filesharing;

import edu.harvard.hms.dbmi.avillach.hpds.data.query.Query;
import edu.harvard.hms.dbmi.avillach.hpds.data.query.ResultType;
import edu.harvard.hms.dbmi.avillach.hpds.processing.AsyncResult;
import edu.harvard.hms.dbmi.avillach.hpds.service.QueryService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.time.Duration;
import java.time.temporal.ChronoUnit;

/**
* Used for sharing data. Given a query, this service will write
* phenotypic and genomic data into a directory
*/
@Service
public class FileSharingService {

private static final Logger LOG = LoggerFactory.getLogger(FileSharingService.class);

@Autowired
private QueryService queryService;

@Autowired
private FileSystemService fileWriter;

@Autowired
private UploadStatusRepository repository;

public boolean createPhenotypicData(Query phenotypicQuery) {
AsyncResult result = queryService.getResultFor(phenotypicQuery.getId());
repository.updatePhenotypicStatus(phenotypicQuery.getId(), UploadStatus.COMPLETE);
return fileWriter.writeResultToFile("phenotypic_data.tsv", phenotypicQuery.getId(), result);
}

public boolean createGenomicData(Query phenotypicQuery) {
Query genomicQuery = createGenomicDataQuery(phenotypicQuery);
repository.updateGenomicStatus(genomicQuery.getId(), UploadStatus.STARTED);
try {
queryService.runQuery(genomicQuery);
} catch (ClassNotFoundException | IOException e) {
LOG.error("Error running genomic query", e);
repository.updateGenomicStatus(genomicQuery.getId(), UploadStatus.ERROR);
return false;
}

AsyncResult status = null;
while (
status == null || // wait for the genomic query to complete
status.status.equals(AsyncResult.Status.PENDING) ||
status.status.equals(AsyncResult.Status.RUNNING)
) {
try {
Thread.sleep(Duration.of(30, ChronoUnit.SECONDS));
} catch (InterruptedException e) {
LOG.error("Error waiting for status: ", e);
}
status = queryService.getStatusFor(genomicQuery.getId());
}
if (status.status != AsyncResult.Status.SUCCESS) {
LOG.error("Error uploading genomic data");
repository.updateGenomicStatus(phenotypicQuery.getId(), UploadStatus.ERROR);
return false;
}

AsyncResult result = queryService.getResultFor(genomicQuery.getId());
repository.updateGenomicStatus(genomicQuery.getId(), UploadStatus.COMPLETE);
return fileWriter.writeResultToFile("genomic_data.tsv", phenotypicQuery.getId(), result);
}

private Query createGenomicDataQuery(Query query) {
Query clone = new Query(query);
clone.setExpectedResultType(ResultType.VCF_EXCERPT);
// I need these queries to be unique, but I don't have access to the UUID gen logic in PIC-SURE from here
// That said, I don't have to abide by UUID restrictions, since this ID isn't getting written to the picsure db
// So appending _genomic to the UUID should be fine.
clone.setId(clone.getId() + "_genomic");
return clone;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package edu.harvard.hms.dbmi.avillach.hpds.service.filesharing;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.nio.file.Path;

@Configuration
public class FileSystemConfig {
@Value("${file_sharing_root:/gic_query_results/}")
private String fileSharingRootDir;

@Value("${enable_file_sharing:false}")
private boolean enableFileSharing;

@Bean()
Path sharingRoot() {
if (!enableFileSharing) {
return Path.of("/dev/null");
}

Path path = Path.of(fileSharingRootDir);
if (!path.toFile().exists()) {
throw new RuntimeException(fileSharingRootDir + " DNE.");
}

return path;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package edu.harvard.hms.dbmi.avillach.hpds.service.filesharing;

import edu.harvard.hms.dbmi.avillach.hpds.processing.AsyncResult;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;

@Service
public class FileSystemService {

private static final Logger LOG = LoggerFactory.getLogger(FileSystemService.class);

@Autowired
private Path sharingRoot;

@Value("${enable_file_sharing:false}")
private boolean enableFileSharing;

public boolean writeResultToFile(String fileName, String directory, AsyncResult result) {
if (!enableFileSharing) {
LOG.warn("Attempted to write query result to file while file sharing is disabled. No-op.");
return false;
}

Path dirPath = Path.of(sharingRoot.toString(), directory);
Path filePath = Path.of(sharingRoot.toString(), directory, fileName);

try {
LOG.info("Writing query {} to file: {}", result.id, filePath);
Files.createDirectory(dirPath);
result.stream.open();
return Files.copy(result.stream, filePath) > 0;
} catch (IOException e) {
LOG.error("Error writing result.", e);
return false;
} finally {
IOUtils.closeQuietly(result.stream);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package edu.harvard.hms.dbmi.avillach.hpds.service.filesharing;

public enum UploadStatus {
STARTED, COMPLETE, ERROR, UNKNOWN;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package edu.harvard.hms.dbmi.avillach.hpds.service.filesharing;

import org.springframework.stereotype.Repository;

@Repository
public class UploadStatusRepository {
public void updatePhenotypicStatus(String queryId, UploadStatus status) {

}

public void updateGenomicStatus(String queryId, UploadStatus status) {

}
}

0 comments on commit ca8b278

Please sign in to comment.