Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions src/main/java/uk/ac/sanger/sccp/stan/ImageDataFileController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package uk.ac.sanger.sccp.stan;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import uk.ac.sanger.sccp.stan.model.Operation;
import uk.ac.sanger.sccp.stan.repo.OperationRepo;
import uk.ac.sanger.sccp.stan.service.imagedatafile.ImageDataFileService;
import uk.ac.sanger.sccp.utils.tsv.TsvFile;

import javax.persistence.EntityNotFoundException;

/**
* Controller for delivering image data files (excel).
* @author dr6
*/
@Controller
public class ImageDataFileController {
public static final String IMAGING_QC_OP_NAME = "Imaging QC";

private final OperationRepo opRepo;
private final ImageDataFileService imageDataFileService;

@Autowired
public ImageDataFileController(OperationRepo opRepo, ImageDataFileService imageDataFileService) {
this.opRepo = opRepo;
this.imageDataFileService = imageDataFileService;
}

@RequestMapping(value="/imageqc", method=RequestMethod.GET,
produces="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
@ResponseBody
public TsvFile<?> getImageDataFile(@RequestParam(name="id") Integer id) {
Operation op = opRepo.findById(id).orElseThrow(() -> new EntityNotFoundException("No operation found with id " + id+"."));
if (!op.getOperationType().getName().equalsIgnoreCase(IMAGING_QC_OP_NAME)) {
throw new IllegalArgumentException("Operation " + id + " is not an imaging QC operation");
}
return imageDataFileService.generateFile(op);
// TsvFileConverter will convert the data to an excel file
}
}
12 changes: 10 additions & 2 deletions src/main/java/uk/ac/sanger/sccp/stan/repo/WorkRepo.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@

import static java.util.stream.Collectors.toMap;
import static java.util.stream.Collectors.toSet;
import static uk.ac.sanger.sccp.utils.BasicUtils.inMap;
import static uk.ac.sanger.sccp.utils.BasicUtils.stream;
import static uk.ac.sanger.sccp.utils.BasicUtils.*;

public interface WorkRepo extends CrudRepository<Work, Integer> {
Optional<Work> findByWorkNumber(String workNumber);
Expand Down Expand Up @@ -83,6 +82,15 @@ default Map<Integer, Set<String>> findWorkNumbersForOpIds(Collection<Integer> op
return opWork;
}

@Query(value = "select work_id from work_op where operation_id=?1", nativeQuery = true)
List<Integer> findWorkIdsForOperationId(Integer opId);

/** Gets list of works linked to the given operation id. */
default List<Work> findWorksForOperationId(Integer opId) {
List<Integer> workIds = findWorkIdsForOperationId(opId);
return (workIds.isEmpty() ? List.of() : asList(findAllById(workIds)));
}

@Query(value="select release_id as releaseId, work_number as workNumber from work_release join work on (work_id=work.id) where release_id IN (?1)", nativeQuery=true)
List<Object[]> _releaseIdWorkNumbersForReleaseIds(Collection<Integer> releaseIds);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package uk.ac.sanger.sccp.stan.service.imagedatafile;

import uk.ac.sanger.sccp.utils.tsv.TsvColumn;

import java.util.function.Function;

/** Columns in the image data file */
public enum ImageDataColumn implements TsvColumn<ImageDataRow> {
location(null),
filename(ImageDataRow::getBarcode),
omero_name(ImageDataRow::getExternalName),
omero_group(ImageDataRow::getOmeroProject),
omero_project(ImageDataRow::getWorkNumber),
omero_dataset(ImageDataRow::getExternalName),
omero_username(ImageDataRow::getUserName),
tags(null),
comments(ImageDataRow::getComments),
;

private final Function<ImageDataRow, String> dataGetter;

ImageDataColumn(Function<ImageDataRow, String> dataGetter) {
this.dataGetter = dataGetter;
}

@Override
public String get(ImageDataRow entry) {
return (dataGetter==null ? null : dataGetter.apply(entry));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package uk.ac.sanger.sccp.stan.service.imagedatafile;

import uk.ac.sanger.sccp.stan.model.Operation;
import uk.ac.sanger.sccp.utils.tsv.TsvFile;

/** Service for generating file data for image qc ops */
public interface ImageDataFileService {
/**
* Generate file data containing the image data for the given operation.
* @param op the operation
* @return the file data
*/
TsvFile<?> generateFile(Operation op);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package uk.ac.sanger.sccp.stan.service.imagedatafile;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import uk.ac.sanger.sccp.stan.model.*;
import uk.ac.sanger.sccp.stan.repo.*;
import uk.ac.sanger.sccp.utils.tsv.TsvColumn;
import uk.ac.sanger.sccp.utils.tsv.TsvFile;

import java.util.*;

import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toSet;
import static uk.ac.sanger.sccp.utils.BasicUtils.*;

/**
* @author dr6
*/
@Service
public class ImageDataFileServiceImp implements ImageDataFileService {
private final WorkRepo workRepo;
private final OperationCommentRepo opComRepo;
private final LabwareRepo lwRepo;

@Autowired
public ImageDataFileServiceImp(WorkRepo workRepo, OperationCommentRepo opComRepo, LabwareRepo lwRepo) {
this.workRepo = workRepo;
this.opComRepo = opComRepo;
this.lwRepo = lwRepo;
}

@Override
public TsvFile<?> generateFile(Operation op) {
String filename = getFilename(op);
List<? extends TsvColumn<ImageDataRow>> columns = getColumns();
List<ImageDataRow> rows = getRows(op);
return new TsvFile<>(filename, rows, columns);
}

/** Filename for the given operation */
public String getFilename(Operation op) {
return "imaging-qc-" + op.getId() + ".xlsx";
}

/** The columns in the file */
public List<ImageDataColumn> getColumns() {
return Arrays.asList(ImageDataColumn.values());
}

/** Gets the comments for the operation, mapped from slot/sample id */
public Map<Integer, List<Comment>> compileCommentMap(Integer opId) {
List<OperationComment> opcoms = opComRepo.findAllByOperationIdIn(List.of(opId));
if (opcoms.isEmpty()) {
return Map.of();
}
Map<Integer, List<Comment>> map = new HashMap<>();
for (OperationComment opCom : opcoms) {
map.computeIfAbsent(opCom.getSampleId(), k -> new ArrayList<>())
.add(opCom.getComment());
}
map.replaceAll((k, v) -> v.stream().sorted(Comparator.comparingInt(Comment::getId)).distinct().toList());
return map;
}

/** Gets the labware for the operation, mapped from their ids */
public Map<Integer, Labware> compileLabwareMap(Operation op) {
Set<Integer> labwareIds = op.getActions().stream()
.map(ac -> ac.getDestination().getLabwareId())
.collect(toSet());
return lwRepo.findAllByIdIn(labwareIds).stream()
.collect(inMap(Labware::getId));
}

/** Gets the rows for the operation */
public List<ImageDataRow> getRows(Operation op) {
List<Work> works = workRepo.findWorksForOperationId(op.getId());
String workNumber = works.stream().map(Work::getWorkNumber).collect(joining(", "));
String omeroProject = works.stream()
.map(Work::getOmeroProject)
.filter(Objects::nonNull)
.distinct()
.map(OmeroProject::getName)
.collect(joining(", "));
Map<Integer, List<Comment>> sampleComments = compileCommentMap(op.getId());
Map<Integer, Labware> lwMap = compileLabwareMap(op);
return compileRows(op, omeroProject, workNumber, op.getUser().getUsername(), lwMap, sampleComments);
}

/** Puts together rows given various data. One row per sample */
public List<ImageDataRow> compileRows(Operation op, String omeroProject, String workNumber, String user,
Map<Integer, Labware> lwMap, Map<Integer, List<Comment>> sampleComments) {
return op.getActions().stream()
.filter(distinctBySerial(ac -> ac.getSample().getId()))
.map(ac -> makeRow(ac, omeroProject, workNumber, user, lwMap, sampleComments))
.distinct()
.toList();
}

/** Converts an action to a row */
public ImageDataRow makeRow(Action ac, String omeroProject, String workNumber, String user,
Map<Integer, Labware> lwMap, Map<Integer, List<Comment>> slotSampleComments) {
Slot slot = ac.getDestination();
Sample sample = ac.getSample();
Labware lw = lwMap.get(slot.getLabwareId());
List<Comment> comments = slotSampleComments.get(sample.getId());
String commentString;
if (nullOrEmpty(comments)) {
commentString = null;
} else {
commentString = comments.stream()
.map(Comment::getText)
.map(s -> s.endsWith(".") ? s : (s + "."))
.collect(joining(" "));
}
return new ImageDataRow(lw.getBarcode(), sample.getTissue().getExternalName(), omeroProject, workNumber, user,
commentString);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package uk.ac.sanger.sccp.stan.service.imagedatafile;

import java.util.Objects;

import static uk.ac.sanger.sccp.utils.BasicUtils.describe;

/**
* @author dr6
*/
public class ImageDataRow {
private String barcode;
private String externalName;
private String omeroProject;
private String workNumber;
private String userName;
private String comments;

public ImageDataRow(String barcode, String externalName, String omeroProject, String workNumber, String userName,
String comments) {
this.barcode = barcode;
this.externalName = externalName;
this.omeroProject = omeroProject;
this.workNumber = workNumber;
this.userName = userName;
this.comments = comments;
}

public String getBarcode() {
return this.barcode;
}

public void setBarcode(String barcode) {
this.barcode = barcode;
}

public String getExternalName() {
return this.externalName;
}

public void setExternalName(String externalName) {
this.externalName = externalName;
}

public String getOmeroProject() {
return this.omeroProject;
}

public void setOmeroProject(String omeroProject) {
this.omeroProject = omeroProject;
}

public String getWorkNumber() {
return this.workNumber;
}

public void setWorkNumber(String workNumber) {
this.workNumber = workNumber;
}

public String getUserName() {
return this.userName;
}

public void setUserName(String userName) {
this.userName = userName;
}

public String getComments() {
return this.comments;
}

public void setComments(String comments) {
this.comments = comments;
}

@Override
public boolean equals(Object o) {
if (o == null || getClass() != o.getClass()) return false;
ImageDataRow that = (ImageDataRow) o;
return (Objects.equals(this.barcode, that.barcode)
&& Objects.equals(this.externalName, that.externalName)
&& Objects.equals(this.omeroProject, that.omeroProject)
&& Objects.equals(this.workNumber, that.workNumber)
&& Objects.equals(this.userName, that.userName)
&& Objects.equals(this.comments, that.comments));
}

@Override
public int hashCode() {
return Objects.hash(barcode, externalName, omeroProject, workNumber, userName, comments);
}

@Override
public String toString() {
return describe(this)
.add("barcode", barcode)
.add("externalName", externalName)
.add("omeroProject", omeroProject)
.add("workNumber", workNumber)
.add("userName", userName)
.add("comments", comments)
.reprStringValues()
.toString();
}
}
2 changes: 2 additions & 0 deletions src/main/java/uk/ac/sanger/sccp/utils/tsv/TsvData.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

/**
* The data required for a tsv file
* @param <C> the type of the columns
* @param <E> the type of the data
* @author dr6
*/
public interface TsvData<C, E> {
Expand Down
Loading
Loading