package org.opencb.opencga.app.cli.main.io;
import org.apache.commons.lang3.StringUtils;
import org.opencb.commons.datastore.core.QueryResponse;
import org.opencb.commons.datastore.core.QueryResult;
import org.opencb.opencga.catalog.models.*;
import org.opencb.opencga.catalog.models.acls.permissions.AbstractAclEntry;
import org.opencb.opencga.catalog.models.acls.permissions.StudyAclEntry;
import org.opencb.opencga.core.common.TimeUtils;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Collectors;
/**
* Created by pfurio on 28/11/16.
*/
public class TextOutputWriter extends AbstractOutputWriter {
public TextOutputWriter() {
}
public TextOutputWriter(WriterConfiguration writerConfiguration) {
super(writerConfiguration);
}
@Override
public void print(QueryResponse queryResponse) {
if (checkErrors(queryResponse)) {
return;
}
if (queryResponse.getResponse().size() == 0 || ((QueryResult) queryResponse.getResponse().get(0)).getNumResults() == 0) {
ps.println("No results found for the query.");
return;
}
ps.print(printMetadata(queryResponse));
List<QueryResult> queryResultList = queryResponse.getResponse();
String[] split = queryResultList.get(0).getResultType().split("\\.");
String clazz = split[split.length - 1];
switch (clazz) {
case "User":
printUser(queryResponse.getResponse());
break;
case "Project":
printProject(queryResponse.getResponse());
break;
case "Study":
printStudy(queryResponse.getResponse());
break;
case "File":
printFiles(queryResponse.getResponse());
break;
case "Sample":
printSamples(queryResponse.getResponse());
break;
case "Cohort":
printCohorts(queryResponse.getResponse());
break;
case "Individual":
printIndividual(queryResponse.getResponse());
break;
case "Job":
printJob(queryResponse.getResponse());
break;
case "VariableSet":
printVariableSet(queryResponse.getResponse());
break;
case "AnnotationSet":
printAnnotationSet(queryResponse.getResponse());
break;
case "FileTree":
printTreeFile(queryResponse);
break;
default:
System.err.println(ANSI_RED + "Error: " + clazz + " not yet supported in text format" + ANSI_RESET);
YamlOutputWriter yamlOutputWriter = new YamlOutputWriter(writerConfiguration);
yamlOutputWriter.print(queryResponse);
break;
}
}
private String printMetadata(QueryResponse queryResponse) {
StringBuilder sb = new StringBuilder();
if (writerConfiguration.isMetadata()) {
int numResults = 0;
// int totalResults = 0;
int time = 0;
List<QueryResult> queryResultList = queryResponse.getResponse();
for (QueryResult queryResult : queryResultList) {
numResults += queryResult.getNumResults();
// totalResults += queryResult.getNumTotalResults();
time += queryResult.getDbTime();
}
sb.append("## Date: ").append(TimeUtils.getTime()).append("\n")
.append("## Number of results: ").append(numResults)
.append(". Time: ").append(time).append(" ms\n");
// TODO: Add query info
sb.append("## Query: { ")
.append(queryResponse.getQueryOptions()
.entrySet().stream().map(entry -> entry.getKey() + ": " + entry.getValue()).collect(Collectors.joining(", ")))
.append(" }\n");
}
return sb.toString();
}
private void printUser(List<QueryResult<User>> queryResultList) {
StringBuilder sb = new StringBuilder();
for (QueryResult<User> queryResult : queryResultList) {
// Write header
if (writerConfiguration.isHeader()) {
sb.append("#(U)ID\tNAME\tE-MAIL\tORGANIZATION\tACCOUNT_TYPE\tSIZE\tQUOTA\n");
sb.append("#(P)\tALIAS\tNAME\tORGANIZATION\tDESCRIPTION\tID\tSIZE\n");
sb.append("#(S)\t\tALIAS\tNAME\tTYPE\tDESCRIPTION\tID\t#GROUPS\tSIZE\n");
}
for (User user : queryResult.getResult()) {
sb.append(String.format("%s%s\t%s\t%s\t%s\t%s\t%d\t%d\n", "", user.getId(), user.getName(), user.getEmail(),
user.getOrganization(), user.getAccount().getType(), user.getSize(), user.getQuota()));
if (user.getProjects().size() > 0) {
for (Project project : user.getProjects()) {
sb.append(String.format("%s%s\t%s\t%s\t%s\t%d\t%d\n", " * ", project.getAlias(), project.getName(),
project.getOrganization(), project.getDescription(), project.getId(), project.getSize()));
if (project.getStudies().size() > 0) {
for (Study study : project.getStudies()) {
sb.append(String.format(" - %s\t%s\t%s\t%s\t%d\t%s\t%d\n", study.getAlias(), study.getName(),
study.getType(), study.getDescription(), study.getId(),
study.getGroups() == null ? ""
: study.getGroups().stream().map(Group::getName).collect(Collectors.joining(",")),
study.getSize()));
if (study.getGroups() != null && study.getGroups().size() > 0) {
sb.append(" Groups:\n");
for (Group group : study.getGroups()) {
printGroup(group, sb, " + ");
}
}
if (study.getAcl().size() > 0) {
sb.append(" Acl:\n");
for (StudyAclEntry studyAclEntry : study.getAcl()) {
printACL(studyAclEntry, sb, " + ");
}
}
}
}
}
}
}
}
ps.println(sb.toString());
}
private void printProject(List<QueryResult<Project>> queryResultList) {
StringBuilder sb = new StringBuilder();
for (QueryResult<Project> queryResult : queryResultList) {
// Write header
sb.append("#ALIAS\tNAME\tID\tORGANIZATION\tORGANISM\tASSEMBLY\tDESCRIPTION\tSIZE\t#STUDIES\tSTATUS\n");
for (Project project : queryResult.getResult()) {
String organism = "NA";
String assembly = "NA";
if (project.getOrganism() != null) {
organism = StringUtils.isNotEmpty(project.getOrganism().getScientificName())
? project.getOrganism().getScientificName()
: (StringUtils.isNotEmpty(project.getOrganism().getCommonName())
? project.getOrganism().getCommonName() : "NA");
if (StringUtils.isNotEmpty(project.getOrganism().getAssembly())) {
assembly = project.getOrganism().getAssembly();
}
}
sb.append(String.format("%s\t%s\t%d\t%s\t%s\t%s\t%s\t%d\t%d\t%s\n", project.getAlias(), project.getName(),
project.getId(), project.getOrganization(), organism, assembly, project.getDescription(), project.getSize(),
project.getStudies().size(), project.getStatus().getName()));
}
}
ps.println(sb.toString());
}
private void printStudy(List<QueryResult<Study>> queryResultList) {
StringBuilder sb = new StringBuilder();
for (QueryResult<Study> queryResult : queryResultList) {
// Write header
sb.append("#ALIAS\tNAME\tTYPE\tDESCRIPTION\tID\t#GROUPS\tSIZE\t#FILES\t#SAMPLES\t#COHORTS\t#INDIVIDUALS\t#JOBS\t")
.append("#VARIABLE_SETS\tSTATUS\n");
for (Study study : queryResult.getResult()) {
sb.append(String.format("%s\t%s\t%s\t%s\t%d\t%d\t%d\t%s\t%d\t%d\t%d\t%d\t%d\t%s\n",
study.getAlias(), study.getName(), study.getType(), study.getDescription(), study.getId(), study.getGroups().size(),
study.getSize(), study.getFiles().size(), study.getSamples().size(), study.getCohorts().size(),
study.getIndividuals().size(), study.getJobs().size(), study.getVariableSets().size(),
study.getStatus().getName()));
}
}
ps.println(sb.toString());
}
private void printGroup(Group group, StringBuilder sb, String prefix) {
sb.append(String.format("%s%s\t%s\n", prefix, group.getName(), StringUtils.join(group.getUserIds(), ", ")));
}
private void printACL(AbstractAclEntry aclEntry, StringBuilder sb, String prefix) {
sb.append(String.format("%s%s\t%s\n", prefix, aclEntry.getMember(), aclEntry.getPermissions().toString()));
}
private void printFiles(List<QueryResult<File>> queryResultList) {
StringBuilder sb = new StringBuilder();
for (QueryResult<File> queryResult : queryResultList) {
// Write header
sb.append("#NAME\tTYPE\tFORMAT\tBIOFORMAT\tDESCRIPTION\tCATALOG_PATH\tFILE_SYSTEM_URI\tID\tSTATUS\tSIZE\tINDEX_STATUS"
+ "\tRELATED_FILES\tSAMPLES\n");
printFiles(queryResult.getResult(), sb, "");
}
ps.println(sb.toString());
}
private void printFiles(List<File> files, StringBuilder sb, String format) {
// # name type format bioformat description path id status size index status related files samples
for (File file : files) {
sb.append(String.format("%s%s\t%s\t%s\t%s\t%s\t%s\t%s\t%d\t%s\t%d\t%s\t%s\t%s\n", format, file.getName(), file.getType(),
file.getFormat(), file.getBioformat(), file.getDescription(), file.getPath(), file.getUri(), file.getId(),
file.getStatus().getName(), file.getSize(), file.getIndex() != null ? file.getIndex().getStatus().getName() : "NA",
StringUtils.join(file.getRelatedFiles().stream().map(File.RelatedFile::getFileId).collect(Collectors.toList()), ", "),
StringUtils.join(file.getSampleIds().stream().collect(Collectors.toList()), ", ")));
}
}
private void printSamples(List<QueryResult<Sample>> queryResultList) {
StringBuilder sb = new StringBuilder();
for (QueryResult<Sample> queryResult : queryResultList) {
// Write header
if (writerConfiguration.isHeader()) {
sb.append("#NAME\tID\tSOURCE\tDESCRIPTION\tSTATUS\tINDIVIDUAL_NAME\tINDIVIDUAL_ID\n");
}
printSamples(queryResult.getResult(), sb, "");
}
ps.println(sb.toString());
}
private void printSamples(List<Sample> samples, StringBuilder sb, String format) {
// # name id source description status individualName individualID
for (Sample sample : samples) {
String individualName = "NA";
String individualId = "NA";
if (sample.getIndividual() != null) {
if (sample.getIndividual().getId() >= 0) {
individualId = Long.toString(sample.getIndividual().getId());
}
if (StringUtils.isNotEmpty(sample.getIndividual().getName())) {
individualName = sample.getIndividual().getName();
}
}
sb.append(String.format("%s%s\t%d\t%s\t%s\t%s\t%s\t%s\n", format, sample.getName(), sample.getId(), sample.getSource(),
sample.getDescription(), sample.getStatus().getName(), individualName, individualId));
}
}
private void printCohorts(List<QueryResult<Cohort>> queryResultList) {
StringBuilder sb = new StringBuilder();
for (QueryResult<Cohort> queryResult : queryResultList) {
// Write header
if (writerConfiguration.isHeader()) {
sb.append("#NAME\tID\tTYPE\tDESCRIPTION\tSTATUS\tTOTAL_SAMPLES\tSAMPLES\tFAMILY\n");
}
for (Cohort cohort : queryResult.getResult()) {
sb.append(String.format("%s\t%d\t%s\t%s\t%s\t%d\t%s\t%s\n", cohort.getName(), cohort.getId(), cohort.getType(),
cohort.getDescription(), cohort.getStatus().getName(), cohort.getSamples().size(),
cohort.getSamples().size() > 0 ? StringUtils.join(cohort.getSamples(), ", ") : "NA",
cohort.getFamily() != null && StringUtils.isNotEmpty(cohort.getFamily().getId()) ? cohort.getFamily().getId() : "NA"));
}
}
ps.println(sb.toString());
}
private void printIndividual(List<QueryResult<Individual>> queryResultList) {
StringBuilder sb = new StringBuilder();
for (QueryResult<Individual> queryResult : queryResultList) {
// Write header
if (writerConfiguration.isHeader()) {
sb.append("#NAME\tID\tFAMILY\tAFFECTATION_STATUS\tSEX\tKARYOTYPIC_SEX\tETHNICITY\tPOPULATION\tSUBPOPULATION\tLIFE_STATUS")
.append("\tSTATUS\tFATHER_ID\tMOTHER_ID\tCREATION_DATE\n");
}
for (Individual individual : queryResult.getResult()) {
String population = "NA";
String subpopulation = "NA";
if (individual.getPopulation() != null) {
if (StringUtils.isNotEmpty(individual.getPopulation().getName())) {
population = individual.getPopulation().getName();
}
if (StringUtils.isNotEmpty(individual.getPopulation().getSubpopulation())) {
subpopulation = individual.getPopulation().getSubpopulation();
}
}
sb.append(String.format("%s\t%d\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n",
individual.getName(), individual.getId(), individual.getFamily(), individual.getAffectationStatus(),
individual.getSex(), individual.getKaryotypicSex(), individual.getEthnicity(), population, subpopulation,
individual.getLifeStatus(), individual.getStatus().getName(),
individual.getFatherId() > 0 ? Long.toString(individual.getFatherId()) : "NA",
individual.getMotherId() > 0 ? Long.toString(individual.getMotherId()) : "NA", individual.getCreationDate()));
}
}
ps.println(sb.toString());
}
private void printJob(List<QueryResult<Job>> queryResultList) {
StringBuilder sb = new StringBuilder();
for (QueryResult<Job> queryResult : queryResultList) {
// Write header
if (writerConfiguration.isHeader()) {
sb.append("#NAME\tID\tTYPE\tTOOL_NAME\tCREATION_DATE\tEXECUTABLE\tEXECUTION\t#VISITS\tSTATUS\tINPUT")
.append("\tOUTPUT\tOUTPUT_DIRECTORY\n");
}
for (Job job : queryResult.getResult()) {
sb.append(String.format("%s\t%d\t%s\t%s\t%s\t%s\t%s\t%d\t%s\t%s\t%s\t%d\n",
job.getName(), job.getId(), job.getType(), job.getToolName(), job.getCreationDate(), job.getExecutable(),
job.getExecution(), job.getVisits(), job.getStatus().getName(), StringUtils.join(job.getInput(), ", "),
StringUtils.join(job.getOutput(), ", "), job.getOutDirId()));
}
}
ps.println(sb.toString());
}
private void printVariableSet(List<QueryResult<VariableSet>> queryResultList) {
StringBuilder sb = new StringBuilder();
for (QueryResult<VariableSet> queryResult : queryResultList) {
// Write header
if (writerConfiguration.isHeader()) {
sb.append("#NAME\tID\tDESCRIPTION\tVARIABLES\n");
}
for (VariableSet variableSet : queryResult.getResult()) {
sb.append(String.format("%s\t%s\t%s\t%s\n", variableSet.getName(), variableSet.getId(), variableSet.getDescription(),
variableSet.getVariables().stream().map(variable -> variable.getName()).collect(Collectors.joining(", "))));
}
}
ps.println(sb.toString());
}
private void printAnnotationSet(List<QueryResult<AnnotationSet>> queryResultList) {
StringBuilder sb = new StringBuilder();
for (QueryResult<AnnotationSet> queryResult : queryResultList) {
for (AnnotationSet annotationSet : queryResult.getResult()) {
// Write header
if (writerConfiguration.isHeader()) {
sb.append("#KEY\tVALUE\n");
}
for (Annotation annotation : annotationSet.getAnnotations()) {
sb.append(String.format("%s\t%s\n", annotation.getName(), annotation.getValue()));
}
}
}
ps.println(sb.toString());
}
private void printTreeFile(QueryResponse<FileTree> queryResponse) {
StringBuilder sb = new StringBuilder();
for (QueryResult<FileTree> fileTreeQueryResult : queryResponse.getResponse()) {
printRecursiveTree(fileTreeQueryResult.getResult(), sb, "");
}
ps.println(sb.toString());
}
private void printRecursiveTree(List<FileTree> fileTreeList, StringBuilder sb, String indent) {
if (fileTreeList == null || fileTreeList.size() == 0) {
return;
}
for (Iterator<FileTree> iterator = fileTreeList.iterator(); iterator.hasNext(); ) {
FileTree fileTree = iterator.next();
File file = fileTree.getFile();
sb.append(String.format("%s %s [%d, %s, %s]\n",
indent.isEmpty() ? "" : indent + (iterator.hasNext() ? "├──" : "└──"),
file.getType() == File.Type.FILE ? file.getName() : file.getName() + "/",
file.getId(),
file.getStatus().getName(),
humanReadableByteCount(file.getSize(), false)));
if (file.getType() == File.Type.DIRECTORY) {
printRecursiveTree(fileTree.getChildren(), sb, indent + (iterator.hasNext()? "│ " : " "));
}
}
}
/**
* Get Bytes numbers in a human readable string
* See http://stackoverflow.com/a/3758880
*
* @param bytes Quantity of bytes
* @param si Use International System (power of 10) or Binary Units (power of 2)
* @return
*/
public static String humanReadableByteCount(long bytes, boolean si) {
int unit = si ? 1000 : 1024;
if (bytes < unit) return bytes + " B";
int exp = (int) (Math.log(bytes) / Math.log(unit));
String pre = (si ? "kMGTPE" : "KMGTPE").charAt(exp-1) + (si ? "" : "i");
return String.format("%.1f %sB", bytes / Math.pow(unit, exp), pre);
}
}