diff --git a/src/main/java/uk/ac/ebi/eva/evaseqcol/controller/seqcol/SeqColController.java b/src/main/java/uk/ac/ebi/eva/evaseqcol/controller/seqcol/SeqColController.java index d88f947..4329501 100644 --- a/src/main/java/uk/ac/ebi/eva/evaseqcol/controller/seqcol/SeqColController.java +++ b/src/main/java/uk/ac/ebi/eva/evaseqcol/controller/seqcol/SeqColController.java @@ -53,7 +53,16 @@ public ResponseEntity getSeqColByDigestAndLevel( required = true) @PathVariable String digest, @Parameter(name = "level", description = "The desired output's level (1 or 2)", - example = "1") @RequestParam(required = false) String level) { + example = "1") @RequestParam(required = false) String level, + @Parameter(name = "metadata", + description = "A boolean value that indicates if we need the metadata of the given seqcol digest", + example = "true, 1, yes") + @RequestParam(required = false, defaultValue = "false") boolean metadata) { + if (metadata) { + return new ResponseEntity<>( + seqColService.getSeqColMetadataBySeqColDigest(digest), HttpStatus.OK + ); + } if (level == null) level = "none"; try { switch (level) { diff --git a/src/main/java/uk/ac/ebi/eva/evaseqcol/entities/SeqColEntity.java b/src/main/java/uk/ac/ebi/eva/evaseqcol/entities/SeqColEntity.java index 05da0ca..585198a 100644 --- a/src/main/java/uk/ac/ebi/eva/evaseqcol/entities/SeqColEntity.java +++ b/src/main/java/uk/ac/ebi/eva/evaseqcol/entities/SeqColEntity.java @@ -17,8 +17,6 @@ public abstract class SeqColEntity { protected String digest; // The level 0 digest - protected NamingConvention namingConvention; - public enum NamingConvention { ENA, GENBANK, UCSC, TEST diff --git a/src/main/java/uk/ac/ebi/eva/evaseqcol/entities/SeqColId.java b/src/main/java/uk/ac/ebi/eva/evaseqcol/entities/SeqColId.java deleted file mode 100644 index 6accb4e..0000000 --- a/src/main/java/uk/ac/ebi/eva/evaseqcol/entities/SeqColId.java +++ /dev/null @@ -1,17 +0,0 @@ -package uk.ac.ebi.eva.evaseqcol.entities; - -import com.sun.istack.NotNull; -import lombok.EqualsAndHashCode; - -import javax.persistence.EnumType; -import javax.persistence.Enumerated; -import java.io.Serializable; - -@EqualsAndHashCode -public class SeqColId implements Serializable { - @NotNull - private String digest; - - @Enumerated(EnumType.STRING) - private SeqColEntity.NamingConvention namingConvention; -} diff --git a/src/main/java/uk/ac/ebi/eva/evaseqcol/entities/SeqColLevelOneEntity.java b/src/main/java/uk/ac/ebi/eva/evaseqcol/entities/SeqColLevelOneEntity.java index dd47949..846e88e 100644 --- a/src/main/java/uk/ac/ebi/eva/evaseqcol/entities/SeqColLevelOneEntity.java +++ b/src/main/java/uk/ac/ebi/eva/evaseqcol/entities/SeqColLevelOneEntity.java @@ -7,20 +7,21 @@ import uk.ac.ebi.eva.evaseqcol.utils.JSONLevelOne; import javax.persistence.Basic; +import javax.persistence.CollectionTable; import javax.persistence.Column; +import javax.persistence.ElementCollection; import javax.persistence.Entity; -import javax.persistence.EnumType; -import javax.persistence.Enumerated; import javax.persistence.FetchType; import javax.persistence.Id; -import javax.persistence.IdClass; +import javax.persistence.JoinColumn; import javax.persistence.Table; +import java.util.HashSet; +import java.util.Set; @Entity @NoArgsConstructor @Data @Table(name = "sequence_collections_L1") -@IdClass(SeqColId.class) public class SeqColLevelOneEntity extends SeqColEntity{ @Id @@ -32,15 +33,19 @@ public class SeqColLevelOneEntity extends SeqColEntity{ @Basic(fetch = FetchType.LAZY) private JSONLevelOne seqColLevel1Object; - @Id - @Column(nullable = false) - @Enumerated(EnumType.STRING) - protected NamingConvention namingConvention; + @ElementCollection(fetch = FetchType.LAZY) + @CollectionTable(name = "seqcol_md", joinColumns = + @JoinColumn(name = "digest", nullable = false, updatable = false)) + private Set metadata; - public SeqColLevelOneEntity(String digest, NamingConvention namingConvention, JSONLevelOne jsonLevelOne){ - super(digest, namingConvention); + public SeqColLevelOneEntity(String digest, JSONLevelOne jsonLevelOne){ + super(digest); this.seqColLevel1Object = jsonLevelOne; - this.namingConvention = namingConvention; + } + + public void addMetadata(SeqColMetadataEntity seqColMetadataEntity){ + if(metadata == null) metadata = new HashSet<>(); + metadata.add(seqColMetadataEntity); } @Override diff --git a/src/main/java/uk/ac/ebi/eva/evaseqcol/entities/SeqColLevelTwoEntity.java b/src/main/java/uk/ac/ebi/eva/evaseqcol/entities/SeqColLevelTwoEntity.java index e707ae7..a7bea68 100644 --- a/src/main/java/uk/ac/ebi/eva/evaseqcol/entities/SeqColLevelTwoEntity.java +++ b/src/main/java/uk/ac/ebi/eva/evaseqcol/entities/SeqColLevelTwoEntity.java @@ -24,9 +24,4 @@ public SeqColLevelTwoEntity setDigest(String digest) { this.digest = digest; return this; } - - public SeqColLevelTwoEntity setNamingConvention(NamingConvention convention) { - this.namingConvention = convention; - return this; - } } diff --git a/src/main/java/uk/ac/ebi/eva/evaseqcol/entities/SeqColMetadataEntity.java b/src/main/java/uk/ac/ebi/eva/evaseqcol/entities/SeqColMetadataEntity.java new file mode 100644 index 0000000..407a081 --- /dev/null +++ b/src/main/java/uk/ac/ebi/eva/evaseqcol/entities/SeqColMetadataEntity.java @@ -0,0 +1,57 @@ +package uk.ac.ebi.eva.evaseqcol.entities; + +import lombok.Data; +import lombok.NoArgsConstructor; +import org.hibernate.annotations.CreationTimestamp; + +import javax.persistence.Column; +import javax.persistence.Embeddable; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; +import java.util.Date; + +@Data +@Embeddable +@NoArgsConstructor +public class SeqColMetadataEntity { + + @Column(name = "source_id") + private String sourceIdentifier; // Eg: INSDC Acession + + @Column(name = "source_url") + private String sourceUrl; + + @Enumerated(EnumType.STRING) + @Column(name = "naming_convention") + private SeqColEntity.NamingConvention namingConvention; + + @Column(name = "created_on", nullable = false) + @Temporal(TemporalType.TIMESTAMP) + @CreationTimestamp + private Date createdOn = new Date(); + + public SeqColMetadataEntity(String sourceIdentifier, String sourceUrl, SeqColEntity.NamingConvention namingConvention, + Date createdOn) { + this.sourceIdentifier = sourceIdentifier; + this.sourceUrl = sourceUrl; + this.namingConvention = namingConvention; + this.createdOn = createdOn; + } + + public SeqColMetadataEntity setNamingConvention(SeqColEntity.NamingConvention namingConvention) { + this.namingConvention = namingConvention; + return this; + } + + public SeqColMetadataEntity setSourceIdentifier(String sourceIdentifier) { + this.sourceIdentifier = sourceIdentifier; + return this; + } + + public SeqColMetadataEntity setSourceUrl(String sourceUrl) { + this.sourceUrl = sourceUrl; + return this; + } +} diff --git a/src/main/java/uk/ac/ebi/eva/evaseqcol/repo/SeqColLevelOneRepository.java b/src/main/java/uk/ac/ebi/eva/evaseqcol/repo/SeqColLevelOneRepository.java index 27ff575..e82e4c0 100644 --- a/src/main/java/uk/ac/ebi/eva/evaseqcol/repo/SeqColLevelOneRepository.java +++ b/src/main/java/uk/ac/ebi/eva/evaseqcol/repo/SeqColLevelOneRepository.java @@ -1,10 +1,13 @@ package uk.ac.ebi.eva.evaseqcol.repo; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; import uk.ac.ebi.eva.evaseqcol.entities.SeqColLevelOneEntity; +import java.util.List; + @Repository public interface SeqColLevelOneRepository extends JpaRepository { SeqColLevelOneEntity findSeqColLevelOneEntityByDigest(String digest); @@ -14,4 +17,10 @@ public interface SeqColLevelOneRepository extends JpaRepository findMetadataBySeqColDigest(String digest); + + @Query(value = "select source_id, source_url, naming_convention, created_on from seqcol_md", nativeQuery = true) + List findAllMetadata(); } diff --git a/src/main/java/uk/ac/ebi/eva/evaseqcol/service/SeqColLevelOneService.java b/src/main/java/uk/ac/ebi/eva/evaseqcol/service/SeqColLevelOneService.java index 2810409..44bc9ae 100644 --- a/src/main/java/uk/ac/ebi/eva/evaseqcol/service/SeqColLevelOneService.java +++ b/src/main/java/uk/ac/ebi/eva/evaseqcol/service/SeqColLevelOneService.java @@ -8,6 +8,7 @@ import uk.ac.ebi.eva.evaseqcol.entities.SeqColLevelOneEntity; import uk.ac.ebi.eva.evaseqcol.digests.DigestCalculator; import uk.ac.ebi.eva.evaseqcol.entities.SeqColLevelTwoEntity; +import uk.ac.ebi.eva.evaseqcol.entities.SeqColMetadataEntity; import uk.ac.ebi.eva.evaseqcol.repo.SeqColLevelOneRepository; import uk.ac.ebi.eva.evaseqcol.utils.JSONExtData; import uk.ac.ebi.eva.evaseqcol.utils.JSONIntegerListExtData; @@ -15,10 +16,13 @@ import uk.ac.ebi.eva.evaseqcol.utils.JSONStringListExtData; import java.io.IOException; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Date; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.stream.Collectors; @Service public class SeqColLevelOneService { @@ -62,12 +66,17 @@ public List getAllSeqColLevelOneObjects(){ /** * Construct a seqCol level 1 entity out of three seqCol level 2 entities that - * hold names, lengths and sequences objects*/ + * hold names, lengths and sequences objects + * TODO: Change the signature of this method and make it accept metadata object instead of namingconvention and source id*/ public SeqColLevelOneEntity constructSeqColLevelOne(List>> stringListExtendedDataEntities, List>> integerListExtendedDataEntities, - SeqColEntity.NamingConvention convention) throws IOException { + SeqColEntity.NamingConvention convention, String sourceId) throws IOException { SeqColLevelOneEntity levelOneEntity = new SeqColLevelOneEntity(); JSONLevelOne jsonLevelOne = new JSONLevelOne(); + SeqColMetadataEntity metadata = new SeqColMetadataEntity() + .setNamingConvention(convention) + .setSourceIdentifier(sourceId); + levelOneEntity.addMetadata(metadata); // Looping over List types for (SeqColExtendedDataEntity> dataEntity: stringListExtendedDataEntities) { @@ -99,14 +108,13 @@ public SeqColLevelOneEntity constructSeqColLevelOne(List> sequencesExtData = new JSONStringListExtData(levelTwoEntity.getSequences()); JSONExtData> lengthsExtData = new JSONIntegerListExtData(levelTwoEntity.getLengths()); @@ -151,7 +159,7 @@ public SeqColLevelOneEntity constructSeqColLevelOne( lengthsExtEntity ); - return constructSeqColLevelOne(stringListExtendedDataEntities,integerListExtendedDataEntities, convention); + return constructSeqColLevelOne(stringListExtendedDataEntities,integerListExtendedDataEntities, convention, sourceId); } /** @@ -208,4 +216,20 @@ public List>> constructIntegerListExtData return integerListExtendedDataEntities; } + public SeqColMetadataEntity transformToMetadataEntity(Object[] tuple) { + return new SeqColMetadataEntity( + (String) tuple[0], + (String) tuple[1], + SeqColEntity.NamingConvention.valueOf((String) tuple[2]), + (Date) tuple[3] + ); + } + + public List getAllMetadata() { + return repository.findAllMetadata().stream().map(this::transformToMetadataEntity).collect(Collectors.toList()); + } + + public List getMetadataBySeqcolDigest(String digest) { + return repository.findMetadataBySeqColDigest(digest).stream().map(this::transformToMetadataEntity).collect(Collectors.toList()); + } } \ No newline at end of file diff --git a/src/main/java/uk/ac/ebi/eva/evaseqcol/service/SeqColService.java b/src/main/java/uk/ac/ebi/eva/evaseqcol/service/SeqColService.java index b3b8f44..c5e009f 100644 --- a/src/main/java/uk/ac/ebi/eva/evaseqcol/service/SeqColService.java +++ b/src/main/java/uk/ac/ebi/eva/evaseqcol/service/SeqColService.java @@ -14,6 +14,7 @@ import uk.ac.ebi.eva.evaseqcol.entities.SeqColLevelOneEntity; import uk.ac.ebi.eva.evaseqcol.entities.SeqColExtendedDataEntity; import uk.ac.ebi.eva.evaseqcol.entities.SeqColLevelTwoEntity; +import uk.ac.ebi.eva.evaseqcol.entities.SeqColMetadataEntity; import uk.ac.ebi.eva.evaseqcol.exception.AssemblyAlreadyIngestedException; import uk.ac.ebi.eva.evaseqcol.exception.AssemblyNotFoundException; import uk.ac.ebi.eva.evaseqcol.exception.AttributeNotDefinedException; @@ -124,6 +125,10 @@ public Optional getSeqColByDigestAndLevel(String digest, } } + public List getSeqColMetadataBySeqColDigest(String digest) { + return levelOneService.getMetadataBySeqcolDigest(digest); + } + /** * Return the service info entity in a Map format * @see 'https://seqcol.readthedocs.io/en/dev/specification/#21-service-info' @@ -165,6 +170,13 @@ public IngestionResultEntity fetchAndInsertAllSeqColInFastaFile(String accession * assembly report. * Return the list of level 0 digests of the inserted seqcol objects*/ public IngestionResultEntity fetchAndInsertAllSeqColByAssemblyAccession(String assemblyAccession) throws IOException { + // Check for existing same source id + boolean sourceIdExists = levelOneService.getAllMetadata().stream() + .anyMatch(md -> md.getSourceIdentifier().equals(assemblyAccession)); + if (sourceIdExists) { + logger.warn("Seqcol objects for assembly " + assemblyAccession + " have been already ingested... Nothing to ingest !"); + throw new AssemblyAlreadyIngestedException(assemblyAccession); + } Optional> seqColDataMap = ncbiSeqColDataSource.getAllPossibleSeqColExtendedData(assemblyAccession); return createSeqColObjectsAndInsert(seqColDataMap, assemblyAccession); } @@ -206,8 +218,8 @@ public IngestionResultEntity createSeqColObjectsAndInsert(Optional seqColDigest = insertSeqColL1AndL2( // TODO: Check for possible self invocation problem @@ -227,7 +239,7 @@ public IngestionResultEntity createSeqColObjectsAndInsert(Optional>>) ucscExtendedDataMap.get("integerListExtDataList"); levelOneEntityUcsc = levelOneService.constructSeqColLevelOne( - extendedStringListDataEntitiesUcsc, extendedIntegerListDataEntitiesUcsc, SeqColEntity.NamingConvention.UCSC); + extendedStringListDataEntitiesUcsc, extendedIntegerListDataEntitiesUcsc, SeqColEntity.NamingConvention.UCSC, GCA_ACCESSION); Optional resultDigestUcsc = seqColService.addFullSequenceCollection( levelOneEntityUcsc, extendedStringListDataEntitiesUcsc, extendedIntegerListDataEntitiesUcsc); if (resultDigestUcsc.isPresent()) { @@ -163,7 +163,7 @@ public void create() throws IOException { extendedIntegerListDataEntitiesGenbank = (List>>) genbankExtendedDataMap.get("integerListExtDataList"); levelOneEntityGenbank = levelOneService.constructSeqColLevelOne( - extendedStringListDataEntitiesGenbank, extendedIntegerListDataEntitiesGenbank, SeqColEntity.NamingConvention.GENBANK); + extendedStringListDataEntitiesGenbank, extendedIntegerListDataEntitiesGenbank, SeqColEntity.NamingConvention.GENBANK, GCA_ACCESSION); Optional resultDigestGenbank = seqColService.addFullSequenceCollection( levelOneEntityGenbank, extendedStringListDataEntitiesGenbank, extendedIntegerListDataEntitiesGenbank); if (resultDigestGenbank.isPresent()) { diff --git a/src/test/java/uk/ac/ebi/eva/evaseqcol/service/SeqColLevelOneServiceTest.java b/src/test/java/uk/ac/ebi/eva/evaseqcol/service/SeqColLevelOneServiceTest.java index 8d0bd66..061716f 100644 --- a/src/test/java/uk/ac/ebi/eva/evaseqcol/service/SeqColLevelOneServiceTest.java +++ b/src/test/java/uk/ac/ebi/eva/evaseqcol/service/SeqColLevelOneServiceTest.java @@ -36,6 +36,8 @@ class SeqColLevelOneServiceTest { @Autowired private AssemblyDataGenerator assemblyDataGenerator; + private final String GCA_ACCESSION = "GCA_000146045.2"; + private AssemblyEntity assemblyEntity; private AssemblySequenceEntity assemblySequenceEntity; @@ -82,10 +84,10 @@ void constructSeqColL1Test() throws IOException { List>> integerListExtDataList = (List>>) extendedDataMapGenbank.get("integerListExtDataList"); SeqColLevelOneEntity levelOneEntity = levelOneService.constructSeqColLevelOne( - stringListExtDataList, integerListExtDataList, SeqColEntity.NamingConvention.GENBANK); + stringListExtDataList, integerListExtDataList, SeqColEntity.NamingConvention.GENBANK, GCA_ACCESSION); SeqColLevelTwoEntity levelTwoEntity = levelTwoService. constructSeqColL2(levelOneEntity.getDigest(), stringListExtDataList, integerListExtDataList); - SeqColLevelOneEntity constructedEntity = levelOneService.constructSeqColLevelOne(levelTwoEntity, SeqColEntity.NamingConvention.GENBANK); + SeqColLevelOneEntity constructedEntity = levelOneService.constructSeqColLevelOne(levelTwoEntity, SeqColEntity.NamingConvention.GENBANK, GCA_ACCESSION); assertNotNull(constructedEntity); assertNotNull(constructedEntity.getSeqColLevel1Object().getSequences()); } @@ -100,7 +102,7 @@ void addSequenceCollectionL1() throws IOException { List>> integerListExtDataList = (List>>) extendedDataMapGenbank.get("integerListExtDataList"); SeqColLevelOneEntity levelOneEntity = levelOneService.constructSeqColLevelOne( - stringListExtDataList, integerListExtDataList, SeqColEntity.NamingConvention.GENBANK); + stringListExtDataList, integerListExtDataList, SeqColEntity.NamingConvention.GENBANK, GCA_ACCESSION); Optional savedEntity = levelOneService.addSequenceCollectionL1(levelOneEntity); assertTrue(savedEntity.isPresent()); System.out.println(savedEntity.get());