Skip to content

Commit

Permalink
x1210 Support bio risk (block registration)
Browse files Browse the repository at this point in the history
Add bio risk entity.
Support admin for bio risks.
Bio risks can be linked to samples.
Copy bio risks from old samples when new samples are created.
Expect bio risk in block registration.
Expect bio risk in block file registration.

Squashed commit of the following:

commit 0b76158
Author: David Robinson <14000840+khelwood@users.noreply.github.com>
Date:   Wed Oct 30 11:51:12 2024 +0000

    Support bio risk in block register excel file

commit 7541d6e
Author: David Robinson <14000840+khelwood@users.noreply.github.com>
Date:   Wed Oct 30 09:59:30 2024 +0000

    Complete the unit tests for BioRiskService

commit 391f2df
Author: David Robinson <14000840+khelwood@users.noreply.github.com>
Date:   Wed Oct 30 09:41:06 2024 +0000

    When an op creates new samples, copy the bio risks from the sources

commit 6f2e43d
Author: David Robinson <14000840+khelwood@users.noreply.github.com>
Date:   Mon Oct 28 15:46:12 2024 +0000

    More support for bio risks, and save bio risks in block registration

commit 4c1f3e5
Author: David Robinson <14000840+khelwood@users.noreply.github.com>
Date:   Thu Oct 24 13:55:39 2024 +0100

    Basic support for BioRisk (biological risk numbers)
  • Loading branch information
khelwood committed Oct 30, 2024
1 parent def02c8 commit 9f5cf88
Show file tree
Hide file tree
Showing 52 changed files with 826 additions and 61 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
</parent>
<groupId>uk.ac.sanger.sccp</groupId>
<artifactId>stan</artifactId>
<version>2.49.0</version>
<version>3.0.0</version>
<name>stan</name>
<description>Spatial Genomics LIMS</description>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ public class GraphQLDataFetchers extends BaseGraphQLResource {
final FixativeRepo fixativeRepo;
final SpeciesRepo speciesRepo;
final HmdmcRepo hmdmcRepo;
final BioRiskRepo bioRiskRepo;
final LabwareRepo labwareRepo;
final ReleaseDestinationRepo releaseDestinationRepo;
final ReleaseRecipientRepo releaseRecipientRepo;
Expand Down Expand Up @@ -92,7 +93,7 @@ public GraphQLDataFetchers(ObjectMapper objectMapper, AuthenticationComponent au
SessionConfig sessionConfig, VersionInfo versionInfo,
TissueTypeRepo tissueTypeRepo, LabwareTypeRepo labwareTypeRepo,
MediumRepo mediumRepo, FixativeRepo fixativeRepo,
SpeciesRepo speciesRepo, HmdmcRepo hmdmcRepo, LabwareRepo labwareRepo,
SpeciesRepo speciesRepo, HmdmcRepo hmdmcRepo, BioRiskRepo bioRiskRepo, LabwareRepo labwareRepo,
ReleaseDestinationRepo releaseDestinationRepo, ReleaseRecipientRepo releaseRecipientRepo,
DestructionReasonRepo destructionReasonRepo, ProjectRepo projectRepo,
ProgramRepo programRepo, CostCodeRepo costCodeRepo, DnapStudyRepo dnapStudyRepo,
Expand Down Expand Up @@ -121,6 +122,7 @@ public GraphQLDataFetchers(ObjectMapper objectMapper, AuthenticationComponent au
this.fixativeRepo = fixativeRepo;
this.speciesRepo = speciesRepo;
this.hmdmcRepo = hmdmcRepo;
this.bioRiskRepo = bioRiskRepo;
this.labwareRepo = labwareRepo;
this.dnapStudyRepo = dnapStudyRepo;
this.solutionRepo = solutionRepo;
Expand Down Expand Up @@ -187,6 +189,10 @@ public DataFetcher<Iterable<Hmdmc>> getHmdmcs() {
return allOrEnabled(hmdmcRepo::findAll, hmdmcRepo::findAllByEnabled);
}

public DataFetcher<Iterable<BioRisk>> getBioRisks() {
return allOrEnabled(bioRiskRepo::findAll, bioRiskRepo::findAllByEnabled);
}

public DataFetcher<Iterable<Fixative>> getFixatives() {
return allOrEnabled(fixativeRepo::findAll, fixativeRepo::findAllByEnabled);
}
Expand Down
12 changes: 11 additions & 1 deletion src/main/java/uk/ac/sanger/sccp/stan/GraphQLMutation.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ public class GraphQLMutation extends BaseGraphQLResource {
final EquipmentAdminService equipmentAdminService;
final DestructionReasonAdminService destructionReasonAdminService;
final HmdmcAdminService hmdmcAdminService;
final BioRiskService bioRiskService;
final ReleaseDestinationAdminService releaseDestinationAdminService;
final ReleaseRecipientAdminService releaseRecipientAdminService;
final SpeciesAdminService speciesAdminService;
Expand Down Expand Up @@ -114,7 +115,7 @@ public GraphQLMutation(ObjectMapper objectMapper, AuthenticationComponent authCo
DestructionService destructionService, SlotCopyService slotCopyService, InPlaceOpService inPlaceOpService,
CommentAdminService commentAdminService, EquipmentAdminService equipmentAdminService,
DestructionReasonAdminService destructionReasonAdminService,
HmdmcAdminService hmdmcAdminService, ReleaseDestinationAdminService releaseDestinationAdminService,
HmdmcAdminService hmdmcAdminService, BioRiskService bioRiskService, ReleaseDestinationAdminService releaseDestinationAdminService,
ReleaseRecipientAdminService releaseRecipientAdminService, SpeciesAdminService speciesAdminService,
ProjectService projectService, ProgramService programService, CostCodeService costCodeService,
FixativeService fixativeService,
Expand Down Expand Up @@ -153,6 +154,7 @@ public GraphQLMutation(ObjectMapper objectMapper, AuthenticationComponent authCo
this.equipmentAdminService = equipmentAdminService;
this.destructionReasonAdminService = destructionReasonAdminService;
this.hmdmcAdminService = hmdmcAdminService;
this.bioRiskService = bioRiskService;
this.releaseDestinationAdminService = releaseDestinationAdminService;
this.releaseRecipientAdminService = releaseRecipientAdminService;
this.speciesAdminService = speciesAdminService;
Expand Down Expand Up @@ -400,6 +402,14 @@ public DataFetcher<Hmdmc> setHmdmcEnabled() {
return adminSetEnabled(hmdmcAdminService::setEnabled, "SetHmdmcEnabled", "hmdmc");
}

public DataFetcher<BioRisk> addBioRisk() {
return adminAdd(bioRiskService::addNew, "AddBioRisk", "code");
}

public DataFetcher<BioRisk> setBioRiskEnabled() {
return adminSetEnabled(bioRiskService::setEnabled, "SetBioRiskEnabled", "code");
}

public DataFetcher<ReleaseDestination> addReleaseDestination() {
return adminAdd(releaseDestinationAdminService::addNew, "AddReleaseDestination", "name");
}
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/uk/ac/sanger/sccp/stan/GraphQLProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ private RuntimeWiring buildWiring() {
.dataFetcher("user", graphQLDataFetchers.getUser())
.dataFetcher("tissueTypes", graphQLDataFetchers.getTissueTypes())
.dataFetcher("hmdmcs", graphQLDataFetchers.getHmdmcs())
.dataFetcher("bioRisks", graphQLDataFetchers.getBioRisks())
.dataFetcher("labwareTypes", graphQLDataFetchers.getLabwareTypes())
.dataFetcher("mediums", graphQLDataFetchers.getMediums())
.dataFetcher("fixatives", graphQLDataFetchers.getFixatives())
Expand Down Expand Up @@ -164,6 +165,8 @@ private RuntimeWiring buildWiring() {
.dataFetcher("renameEquipment", transact(graphQLMutation.renameEquipment()))
.dataFetcher("addHmdmc", graphQLMutation.addHmdmc()) // internal transaction
.dataFetcher("setHmdmcEnabled", transact(graphQLMutation.setHmdmcEnabled()))
.dataFetcher("addBioRisk", graphQLMutation.addBioRisk()) // internal transaction
.dataFetcher("setBioRiskEnabled", transact(graphQLMutation.setBioRiskEnabled()))
.dataFetcher("addDestructionReason", graphQLMutation.addDestructionReason()) // internal transaction
.dataFetcher("setDestructionReasonEnabled", transact(graphQLMutation.setDestructionReasonEnabled()))
.dataFetcher("addReleaseDestination", graphQLMutation.addReleaseDestination()) // internal transaction
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,12 @@ public Validator<String> roiValidator() {
return new StringValidator("ROI", 1, 64, charTypes);
}

@Bean
public Validator<String> bioRiskCodeValidator() {
Set<CharacterType> charTypes = EnumSet.of(CharacterType.ALPHA, CharacterType.DIGIT, CharacterType.UNDERSCORE);
return new StringValidator("Bio risk code", 2, 20, charTypes);
}

@Bean
public Clock clock() {
return Clock.systemUTC();
Expand Down
90 changes: 90 additions & 0 deletions src/main/java/uk/ac/sanger/sccp/stan/model/BioRisk.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package uk.ac.sanger.sccp.stan.model;

import javax.persistence.*;
import java.util.Objects;

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

/**
* Biological risk assessment number.
* @author dr6
*/
@Entity
public class BioRisk implements HasIntId, HasEnabled {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String code;
private boolean enabled = true;

public BioRisk() {} // required no-arg constructor

public BioRisk(Integer id, String code, boolean enabled) {
this.id = id;
this.code = code;
this.enabled = enabled;
}

public BioRisk(Integer id, String code) {
this(id, code, true);
}

public BioRisk(String code) {
this(null, code, true);
}

/** Primary key */
@Override
public Integer getId() {
return this.id;
}

public void setId(Integer id) {
this.id = id;
}

/** The alphanumeric code representing this risk assessment. */
public String getCode() {
return this.code;
}

public void setCode(String code) {
this.code = code;
}

@Override
public boolean isEnabled() {
return this.enabled;
}

@Override
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}

@Override
public String toString() {
return describe(this)
.add("id", id)
.add("code", code)
.add("enabled", enabled)
.reprStringValues()
.toString();
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || o.getClass() != this.getClass()) return false;
BioRisk that = (BioRisk) o;
return (this.enabled == that.enabled
&& Objects.equals(this.id, that.id)
&& Objects.equals(this.code, that.code)
);
}

@Override
public int hashCode() {
return (id != null ? id.hashCode() : code !=null ? code.hashCode() : 0);
}
}
96 changes: 96 additions & 0 deletions src/main/java/uk/ac/sanger/sccp/stan/repo/BioRiskRepo.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package uk.ac.sanger.sccp.stan.repo;

import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import uk.ac.sanger.sccp.stan.model.BioRisk;
import uk.ac.sanger.sccp.stan.model.Sample;

import javax.persistence.EntityNotFoundException;
import java.util.*;

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

/** Repo for {@link BioRisk} */
public interface BioRiskRepo extends CrudRepository<BioRisk, Integer> {
/** Finds the bio risk with the given code, if it exists. */
Optional<BioRisk> findByCode(String code);

/**
* Gets the bio risk with the given code.
* @param code the code of the bio risk to get
* @return the bio risk with the given code
* @exception EntityNotFoundException if no such bio risk exists
*/
default BioRisk getByCode(String code) throws EntityNotFoundException {
return findByCode(code).orElseThrow(() -> new EntityNotFoundException("Unknown bio risk code: "+repr(code)));
}

List<BioRisk> findAllByCodeIn(Collection<String> codes);

/** Finds all bio risks matching the given value for enabled. */
List<BioRisk> findAllByEnabled(boolean enabled);

@Query(value = "select bio_risk_id from sample_bio_risk where sample_id=?", nativeQuery = true)
Integer loadBioRiskIdForSampleId(int sampleId);

@Query(value = "select sample_id, bio_risk_id from sample_bio_risk where sample_id in (?1)", nativeQuery = true)
int[][] _loadBioRiskIdsForSampleIds(Collection<Integer> sampleIds);

/** Loads the bio risk (if any) linked to the specified sample */
default Optional<BioRisk> loadBioRiskForSampleId(int sampleId) {
Integer bioRiskId = loadBioRiskIdForSampleId(sampleId);
return bioRiskId == null ? Optional.empty() : findById(bioRiskId);
}

/**
* Loads the bio risk ids linked to the given sample ids
* @param sampleIds sample ids
* @return a map of sample id to bio risk id, omitting missing values
*/
default Map<Integer, Integer> loadBioRiskIdsForSampleIds(Collection<Integer> sampleIds) {
int[][] sambr = _loadBioRiskIdsForSampleIds(sampleIds);
if (sambr == null || sambr.length==0) {
return Map.of();
}
return Arrays.stream(sambr)
.collect(toMap(arr -> arr[0], arr -> arr[1]));
}

/**
* Loads the bio risks linked to the given sample ids
* @param sampleIds sample ids
* @return a map of sample id to bio risk, omitting missing values
*/
default Map<Integer, BioRisk> loadBioRisksForSampleIds(Collection<Integer> sampleIds) {
int[][] sambr = _loadBioRiskIdsForSampleIds(sampleIds);
if (sambr==null || sambr.length==0) {
return Map.of();
}
Set<Integer> bioRiskIds = Arrays.stream(sambr)
.map(arr -> arr[1])
.collect(toSet());
Map<Integer, BioRisk> idBioRisks = stream(findAllById(bioRiskIds))
.collect(inMap(BioRisk::getId));
return Arrays.stream(sambr)
.collect(toMap(arr -> arr[0], arr -> idBioRisks.get(arr[1])));
}

/**
* Records the given bio risk id against the given sample id and operation id
* @param sampleId sample id
* @param bioRiskId bio risk id
* @param opId operation id
*/
@Modifying
@Query(value = "insert INTO sample_bio_risk (sample_id, bio_risk_id, operation_id) " +
"values (?, ?, ?)", nativeQuery = true)
void recordBioRisk(int sampleId, int bioRiskId, Integer opId);

/** Links the given bio risk to the given sample and operation */
default void recordBioRisk(Sample sample, BioRisk bioRisk, int opId) {
recordBioRisk(sample.getId(), bioRisk.getId(), opId);
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package uk.ac.sanger.sccp.stan.request.register;

import com.google.common.base.MoreObjects;
import uk.ac.sanger.sccp.stan.model.LifeStage;

import java.time.LocalDate;
import java.util.Objects;

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

/**
* The information required to register a block.
* @author dr6
Expand All @@ -25,6 +26,7 @@ public class BlockRegisterRequest {
private String species;
private boolean existingTissue;
private LocalDate sampleCollectionDate;
private String bioRiskCode;

public String getDonorIdentifier() {
return this.donorIdentifier;
Expand Down Expand Up @@ -138,6 +140,14 @@ public void setSampleCollectionDate(LocalDate sampleCollectionDate) {
this.sampleCollectionDate = sampleCollectionDate;
}

public String getBioRiskCode() {
return this.bioRiskCode;
}

public void setBioRiskCode(String bioRiskCode) {
this.bioRiskCode = bioRiskCode;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
Expand All @@ -157,6 +167,7 @@ public boolean equals(Object o) {
&& Objects.equals(this.fixative, that.fixative)
&& Objects.equals(this.species, that.species)
&& Objects.equals(this.sampleCollectionDate, that.sampleCollectionDate)
&& Objects.equals(this.bioRiskCode, that.bioRiskCode)
);
}

Expand All @@ -167,7 +178,7 @@ public int hashCode() {

@Override
public String toString() {
return MoreObjects.toStringHelper(this)
return describe(this)
.add("donorIdentifier", donorIdentifier)
.add("lifeStage", lifeStage)
.add("hmdmc", hmdmc)
Expand All @@ -182,6 +193,8 @@ public String toString() {
.add("species", species)
.add("existingTissue", existingTissue)
.add("sampleCollectionDate", sampleCollectionDate)
.add("bioRiskCode", bioRiskCode)
.reprStringValues()
.toString();
}
}
Loading

0 comments on commit 9f5cf88

Please sign in to comment.