Skip to content

Commit

Permalink
Fixed bug where modify flow would not insert attributes between some …
Browse files Browse the repository at this point in the history
…attribute and a sequence attribute
  • Loading branch information
KarlSjostrand committed Mar 22, 2018
1 parent 28dab91 commit 75100d7
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 22 deletions.
49 changes: 27 additions & 22 deletions src/main/scala/se/nimsa/dicom/streams/DicomModifyFlow.scala
Original file line number Diff line number Diff line change
Expand Up @@ -106,37 +106,42 @@ object DicomModifyFlow {
def isInDataset(tagToTest: TagPath, sequenceMaybe: Option[TagPathSequence]): Boolean =
sequenceMaybe.map(tagToTest.startsWithSubPath).getOrElse(tagToTest.isRoot)

def findInsertParts: List[DicomPart] = sortedModifications
.filter(_.insert)
.filter(m => tagPath.exists(tp => isBetween(m.tagPath, tp, latestTagPath)))
.filter(m => isInDataset(m.tagPath, tagPath.flatMap(_.previous)))
.flatMap(m => headerAndValueParts(m.tagPath, m.modification))

def findModifyPart(header: DicomHeader): List[DicomPart] = sortedModifications
.find(m => tagPath.exists(m.matches))
.map { tagModification =>
if (header.length > 0) {
currentHeader = Some(header)
currentModification = Some(tagModification)
value = ByteString.empty
Nil
} else {
val newValue = tagModification.modification(ByteString.empty)
val newHeader = header.withUpdatedLength(newValue.length)
val chunkOrNot = if (newValue.nonEmpty) DicomValueChunk(bigEndian, newValue, last = true) :: Nil else Nil
newHeader :: chunkOrNot
}
}
.getOrElse(header :: Nil)

override def onPart(part: DicomPart): List[DicomPart] =
part match {
case header: DicomHeader =>
updateSyntax(header)
val insertParts = sortedModifications
.filter(_.insert)
.filter(m => tagPath.exists(tp => isBetween(m.tagPath, tp, latestTagPath)))
.filter(m => isInDataset(m.tagPath, tagPath.flatMap(_.previous)))
.flatMap(m => headerAndValueParts(m.tagPath, m.modification))
val modifyPart = sortedModifications
.find(m => tagPath.exists(m.matches))
.map { tagModification =>
if (header.length > 0) {
currentHeader = Some(header)
currentModification = Some(tagModification)
value = ByteString.empty
Nil
} else {
val newValue = tagModification.modification(ByteString.empty)
val newHeader = header.withUpdatedLength(newValue.length)
val chunkOrNot = if (newValue.nonEmpty) DicomValueChunk(bigEndian, newValue, last = true) :: Nil else Nil
newHeader :: chunkOrNot
}
}
.getOrElse(header :: Nil)
val insertParts = findInsertParts
val modifyPart = findModifyPart(header)
latestTagPath = tagPath
insertParts ::: modifyPart

case part: DicomSequence =>
val insertParts = findInsertParts
latestTagPath = tagPath
part :: Nil
insertParts ::: part :: Nil

case chunk: DicomValueChunk =>
if (currentModification.isDefined && currentHeader.isDefined) {
Expand Down
3 changes: 3 additions & 0 deletions src/test/scala/se/nimsa/dicom/TestData.scala
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ object TestData {
val patientNameJohnDoeBE: ByteString = tagToBytesBE(0x00100010) ++ ByteString("PN") ++ shortToBytesBE(0x0008) ++ ByteString("John^Doe")
val patientNameJohnDoeImplicit: ByteString = tagToBytesLE(0x00100010) ++ intToBytesLE(0x00000008) ++ ByteString("John^Doe")
val emptyPatientName: ByteString = tagToBytesLE(0x00100010) ++ ByteString("PN") ++ shortToBytesLE(0x0000)

val patientID: ByteString = tagToBytesLE(0x00100020) ++ ByteString("LO") ++ shortToBytesLE(0x0008) ++ ByteString("12345678")

val studyDate: ByteString = tagToBytesLE(0x00080020) ++ ByteString("DA") ++ shortToBytesLE(0x0008) ++ ByteString("19700101")

def item(): ByteString = item(0xFFFFFFFF)
Expand Down
51 changes: 51 additions & 0 deletions src/test/scala/se/nimsa/dicom/streams/DicomModifyFlowTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,57 @@ class DicomModifyFlowTest extends TestKit(ActorSystem("DicomFlowSpec")) with Fla
.expectDicomComplete()
}

it should "insert attributes between a normal attribute and a sequence attribute" in {
val bytes = studyDate ++ sequence(Tag.AbstractPriorCodeSequence) ++ sequenceEnd

val source = Source.single(bytes)
.via(new DicomParseFlow())
.via(modifyFlow(TagModification.contains(TagPath.fromTag(Tag.PatientName), _ => patientNameJohnDoe.drop(8), insert = true)))

source.runWith(TestSink.probe[DicomPart])
.expectHeader(Tag.StudyDate, VR.DA, studyDate.length - 8)
.expectValueChunk(studyDate.drop(8))
.expectHeader(Tag.PatientName, VR.PN, patientNameJohnDoe.length - 8)
.expectValueChunk(patientNameJohnDoe.drop(8))
.expectSequence(Tag.AbstractPriorCodeSequence)
.expectSequenceDelimitation()
.expectDicomComplete()
}

it should "insert attributes between a sequence attribute and a normal attribute" in {
val bytes = sequence(Tag.DerivationCodeSequence) ++ sequenceEnd ++ patientID

val source = Source.single(bytes)
.via(new DicomParseFlow())
.via(modifyFlow(TagModification.contains(TagPath.fromTag(Tag.PatientName), _ => patientNameJohnDoe.drop(8), insert = true)))

source.runWith(TestSink.probe[DicomPart])
.expectSequence(Tag.DerivationCodeSequence)
.expectSequenceDelimitation()
.expectHeader(Tag.PatientName, VR.PN, patientNameJohnDoe.length - 8)
.expectValueChunk(patientNameJohnDoe.drop(8))
.expectHeader(Tag.PatientID, VR.LO, patientID.length - 8)
.expectValueChunk(patientID.drop(8))
.expectDicomComplete()
}

it should "insert attributes between two sequence attributes" in {
val bytes = sequence(Tag.DerivationCodeSequence) ++ sequenceEnd ++ sequence(Tag.AbstractPriorCodeSequence) ++ sequenceEnd

val source = Source.single(bytes)
.via(new DicomParseFlow())
.via(modifyFlow(TagModification.contains(TagPath.fromTag(Tag.PatientName), _ => patientNameJohnDoe.drop(8), insert = true)))

source.runWith(TestSink.probe[DicomPart])
.expectSequence(Tag.DerivationCodeSequence)
.expectSequenceDelimitation()
.expectHeader(Tag.PatientName, VR.PN, patientNameJohnDoe.length - 8)
.expectValueChunk(patientNameJohnDoe.drop(8))
.expectSequence(Tag.AbstractPriorCodeSequence)
.expectSequenceDelimitation()
.expectDicomComplete()
}

it should "modify, not insert, when 'insert' attributes are already present" in {
val bytes = studyDate ++ patientNameJohnDoe

Expand Down

0 comments on commit 75100d7

Please sign in to comment.