public async Task <IEnumerable <FhirTransactionRequestEntry> > BuildAsync(FhirTransactionContext context, CancellationToken cancellationToken)
        {
            EnsureArg.IsNotNull(context, nameof(context));
            EnsureArg.IsNotNull(context.ChangeFeedEntry, nameof(context.ChangeFeedEntry));

            Identifier         identifier           = IdentifierUtility.CreateIdentifier(context.ChangeFeedEntry.StudyInstanceUid);
            List <Observation> matchingObservations = (await _fhirService.RetrieveObservationsAsync(identifier, cancellationToken)).ToList();

            // terminate early if no observation found
            if (matchingObservations.Count == 0)
            {
                return(null);
            }

            var requests = new List <FhirTransactionRequestEntry>();

            foreach (Observation observation in matchingObservations)
            {
                Bundle.RequestComponent request = new Bundle.RequestComponent()
                {
                    Method = Bundle.HTTPVerb.DELETE,
                    Url    = $"{ResourceType.Observation.GetLiteral()}/{observation.Id}"
                };

                requests.Add(new FhirTransactionRequestEntry(
                                 FhirTransactionRequestMode.Delete,
                                 request,
                                 observation.ToServerResourceId(),
                                 observation));
            }

            return(requests);
        }
        public void GivenStudyInstanceUid_WhenCreated_ThenCorrectIdentifierShouldBeCreated()
        {
            var identifier = IdentifierUtility.CreateIdentifier("123");

            Assert.NotNull(identifier);
            Assert.Equal("urn:dicom:uid", identifier.System);
            Assert.Equal("urn:oid:123", identifier.Value);
        }
        /// <inheritdoc/>
        public async Task <FhirTransactionRequestEntry> BuildAsync(FhirTransactionContext context, CancellationToken cancellationToken)
        {
            EnsureArg.IsNotNull(context, nameof(context));
            EnsureArg.IsNotNull(context.ChangeFeedEntry, nameof(context.ChangeFeedEntry));

            ChangeFeedEntry changeFeedEntry = context.ChangeFeedEntry;

            Identifier   imagingStudyIdentifier = IdentifierUtility.CreateIdentifier(changeFeedEntry.StudyInstanceUid);
            ImagingStudy imagingStudy           = await _fhirService.RetrieveImagingStudyAsync(imagingStudyIdentifier, cancellationToken);

            // Returns null if imagingStudy does not exists for given studyInstanceUid
            if (imagingStudy == null)
            {
                return(null);
            }

            string imagingStudySource = imagingStudy.Meta.Source;

            ImagingStudy.SeriesComponent   series   = ImagingStudyPipelineHelper.GetSeriesWithinAStudy(changeFeedEntry.SeriesInstanceUid, imagingStudy.Series);
            ImagingStudy.InstanceComponent instance = ImagingStudyPipelineHelper.GetInstanceWithinASeries(changeFeedEntry.SopInstanceUid, series);

            // Return null if the given instance is not present in ImagingStudy
            if (instance == null)
            {
                return(null);
            }

            // Removes instance from series collection
            series.Instance.Remove(instance);

            // Removes series from ImagingStudy if its instance collection is empty
            if (series.Instance.Count == 0)
            {
                imagingStudy.Series.Remove(series);
            }

            if (imagingStudy.Series.Count == 0 && _dicomWebEndpoint.Equals(imagingStudySource, System.StringComparison.Ordinal))
            {
                return(new FhirTransactionRequestEntry(
                           FhirTransactionRequestMode.Delete,
                           ImagingStudyPipelineHelper.GenerateDeleteRequest(imagingStudy),
                           imagingStudy.ToServerResourceId(),
                           imagingStudy));
            }

            return(new FhirTransactionRequestEntry(
                       FhirTransactionRequestMode.Update,
                       ImagingStudyPipelineHelper.GenerateUpdateRequest(imagingStudy),
                       imagingStudy.ToServerResourceId(),
                       imagingStudy));
        }
Example #4
0
        public async Task <IEnumerable <FhirTransactionRequestEntry> > BuildAsync(FhirTransactionContext context, CancellationToken cancellationToken)
        {
            EnsureArg.IsNotNull(context?.ChangeFeedEntry, nameof(context.ChangeFeedEntry));
            EnsureArg.IsNotNull(context.Request, nameof(context.Request));

            IResourceId     patientId       = context.Request.Patient.ResourceId;
            IResourceId     imagingStudyId  = context.Request.ImagingStudy.ResourceId;
            ChangeFeedEntry changeFeedEntry = context.ChangeFeedEntry;

            Identifier identifier = IdentifierUtility.CreateIdentifier(changeFeedEntry.StudyInstanceUid);

            IReadOnlyCollection <Observation> observations = _observationParser.Parse(changeFeedEntry.Metadata, patientId.ToResourceReference(), imagingStudyId.ToResourceReference(), identifier);

            if (observations.Count == 0)
            {
                return(Enumerable.Empty <FhirTransactionRequestEntry>());
            }

            Identifier imagingStudyIdentifier = imagingStudyId.ToResourceReference().Identifier;
            IEnumerable <Observation> existingDoseSummariesAsync = imagingStudyIdentifier != null
                ? await _fhirService
                                                                   .RetrieveObservationsAsync(
                imagingStudyId.ToResourceReference().Identifier,
                cancellationToken)
                : new List <Observation>();

            // TODO: Figure out a way to match existing observations with newly created ones.

            List <FhirTransactionRequestEntry> fhirRequests = new List <FhirTransactionRequestEntry>();

            foreach (var observation in observations)
            {
                fhirRequests.Add(new FhirTransactionRequestEntry(
                                     FhirTransactionRequestMode.Create,
                                     new Bundle.RequestComponent()
                {
                    Method = Bundle.HTTPVerb.POST,
                    Url    = ResourceType.Observation.GetLiteral()
                },
                                     new ClientResourceId(),
                                     observation));
            }

            return(fhirRequests);
        }
        public void RadiationEventWithAllSupportedAttributes()
        {
            const string  randomIrradiationEventUid          = "1.2.3.4.5.6.123123";
            const decimal randomDecimalNumber                = (decimal)0.10;
            var           randomRadiationMeasurementCodeItem = new DicomCodeItem("mGy", "UCUM", "mGy");
            var           report = new DicomStructuredReport(
                ObservationConstants.IrradiationEventXRayData,
                new DicomContentItem(
                    ObservationConstants.IrradiationEventUid,
                    DicomRelationship.Contains,
                    new DicomUID(randomIrradiationEventUid, "", DicomUidType.Unknown)),
                new DicomContentItem(
                    ObservationConstants.MeanCtdIvol,
                    DicomRelationship.Contains,
                    new DicomMeasuredValue(randomDecimalNumber,
                                           randomRadiationMeasurementCodeItem)),
                new DicomContentItem(
                    ObservationConstants.Dlp,
                    DicomRelationship.Contains,
                    new DicomMeasuredValue(randomDecimalNumber,
                                           randomRadiationMeasurementCodeItem)),
                new DicomContentItem(
                    ObservationConstants.CtdIwPhantomType,
                    DicomRelationship.Contains,
                    new DicomCodeItem("113691", "DCM", "IEC Body Dosimetry Phantom")));

            var observations = _observationParser.Parse(
                report.Dataset,
                new ResourceReference(),
                new ResourceReference(),
                IdentifierUtility.CreateIdentifier(randomIrradiationEventUid));

            Assert.Single(observations);

            Observation radiationEvent = observations.First();

            Assert.Single(radiationEvent.Identifier);
            Assert.Equal("urn:oid:" + randomIrradiationEventUid, radiationEvent.Identifier[0].Value);
            Assert.Equal(2,
                         radiationEvent.Component
                         .Count(component => component.Value is Quantity));
            Assert.Equal(1,
                         radiationEvent.Component
                         .Count(component => component.Value is CodeableConcept));
        }
        public static ImagingStudy ValidateImagingStudyUpdate(string studyInstanceUid, string patientResourceId, FhirTransactionRequestEntry entry, bool hasAccessionNumber = true)
        {
            Identifier expectedIdentifier = IdentifierUtility.CreateIdentifier(studyInstanceUid);
            string     expectedRequestUrl = $"ImagingStudy/{entry.Resource.Id}";

            ValidateRequestEntryMinimumRequirementForWithChange(FhirTransactionRequestMode.Update, expectedRequestUrl, Bundle.HTTPVerb.PUT, actualEntry: entry);

            ImagingStudy updatedImagingStudy = Assert.IsType <ImagingStudy>(entry.Resource);

            Assert.Equal(ImagingStudy.ImagingStudyStatus.Available, updatedImagingStudy.Status);

            ValidateResourceReference(patientResourceId, updatedImagingStudy.Subject);

            Action <Identifier> studyIdValidaion          = identifier => ValidateIdentifier("urn:dicom:uid", $"urn:oid:{studyInstanceUid}", identifier);
            Action <Identifier> accessionNumberValidation = identifier => ValidateAccessionNumber(null, FhirTransactionContextBuilder.DefaultAccessionNumber, identifier);

            Assert.Collection(
                updatedImagingStudy.Identifier,
                hasAccessionNumber ? new[] { studyIdValidaion, accessionNumberValidation } : new[] { studyIdValidaion });

            return(updatedImagingStudy);
        }
Example #7
0
        public static ImagingStudy CreateNewImagingStudy(string studyInstanceUid, List <string> seriesInstanceUidList, List <string> sopInstanceUidList, string patientResourceId, string source = "defaultSouce")
        {
            // Create a new ImagingStudy
            ImagingStudy study = new ImagingStudy
            {
                Id      = "123",
                Status  = ImagingStudy.ImagingStudyStatus.Available,
                Subject = new ResourceReference(patientResourceId),
                Meta    = new Meta()
                {
                    VersionId = "1",
                    Source    = source,
                },
            };

            foreach (string seriesInstanceUid in seriesInstanceUidList)
            {
                ImagingStudy.SeriesComponent series = new ImagingStudy.SeriesComponent()
                {
                    Uid = seriesInstanceUid,
                };

                foreach (string sopInstanceUid in sopInstanceUidList)
                {
                    ImagingStudy.InstanceComponent instance = new ImagingStudy.InstanceComponent()
                    {
                        Uid = sopInstanceUid,
                    };

                    series.Instance.Add(instance);
                }

                study.Series.Add(series);
            }

            study.Identifier.Add(IdentifierUtility.CreateIdentifier(studyInstanceUid));

            return(study);
        }
Example #8
0
        /// <inheritdoc/>
        public async Task <FhirTransactionRequestEntry> BuildAsync(FhirTransactionContext context, CancellationToken cancellationToken)
        {
            EnsureArg.IsNotNull(context, nameof(context));
            EnsureArg.IsNotNull(context.ChangeFeedEntry, nameof(context.ChangeFeedEntry));
            EnsureArg.IsNotNull(context.Request, nameof(context.Request));

            IResourceId patientId = context.Request.Patient.ResourceId;

            ChangeFeedEntry changeFeedEntry = context.ChangeFeedEntry;

            Identifier imagingStudyIdentifier = IdentifierUtility.CreateIdentifier(changeFeedEntry.StudyInstanceUid);

            ImagingStudy existingImagingStudy = await _fhirService.RetrieveImagingStudyAsync(imagingStudyIdentifier, cancellationToken);

            ImagingStudy imagingStudy = (ImagingStudy)existingImagingStudy?.DeepCopy();

            FhirTransactionRequestMode requestMode = FhirTransactionRequestMode.None;

            if (existingImagingStudy == null)
            {
                imagingStudy = new ImagingStudy()
                {
                    Status  = ImagingStudy.ImagingStudyStatus.Available,
                    Subject = patientId.ToResourceReference(),
                };

                imagingStudy.Identifier.Add(imagingStudyIdentifier);
                imagingStudy.Meta = new Meta()
                {
                    Source = _dicomWebEndpoint,
                };
                requestMode = FhirTransactionRequestMode.Create;
            }

            await SynchronizeImagingStudyPropertiesAsync(context, imagingStudy, cancellationToken);

            if (requestMode != FhirTransactionRequestMode.Create &&
                !existingImagingStudy.IsExactly(imagingStudy))
            {
                requestMode = FhirTransactionRequestMode.Update;
            }

            Bundle.RequestComponent request = requestMode switch
            {
                FhirTransactionRequestMode.Create => ImagingStudyPipelineHelper.GenerateCreateRequest(imagingStudyIdentifier),
                FhirTransactionRequestMode.Update => ImagingStudyPipelineHelper.GenerateUpdateRequest(imagingStudy),
                _ => null,
            };

            IResourceId resourceId = requestMode switch
            {
                FhirTransactionRequestMode.Create => new ClientResourceId(),
                _ => existingImagingStudy.ToServerResourceId(),
            };

            return(new FhirTransactionRequestEntry(
                       requestMode,
                       request,
                       resourceId,
                       imagingStudy));
        }
        public void DoseSummaryWithAllSupportedAttributes()
        {
            const string  studyInstanceUid    = "1.3.12.2.123.5.4.5.123123.123123";
            const string  accessionNumber     = "random-accession";
            const decimal randomDecimalNumber = (decimal)0.10;
            var           randomRadiationMeasurementCodeItem = new DicomCodeItem("mGy", "UCUM", "mGy");

            var report = new DicomStructuredReport(
                ObservationConstants.RadiopharmaceuticalRadiationDoseReport,
                // identifiers
                new DicomContentItem(
                    ObservationConstants.StudyInstanceUid,
                    DicomRelationship.HasProperties,
                    new DicomUID(studyInstanceUid, "", DicomUidType.Unknown)),
                new DicomContentItem(
                    ObservationConstants.AccessionNumber,
                    DicomRelationship.HasProperties,
                    DicomValueType.Text,
                    accessionNumber),

                // attributes
                new DicomContentItem(
                    ObservationConstants.DoseRpTotal,
                    DicomRelationship.Contains,
                    new DicomMeasuredValue(randomDecimalNumber,
                                           randomRadiationMeasurementCodeItem)),
                new DicomContentItem(
                    ObservationConstants.AccumulatedAverageGlandularDose,
                    DicomRelationship.Contains,
                    new DicomMeasuredValue(randomDecimalNumber,
                                           randomRadiationMeasurementCodeItem)),
                new DicomContentItem(
                    ObservationConstants.DoseAreaProductTotal,
                    DicomRelationship.Contains,
                    new DicomMeasuredValue(randomDecimalNumber,
                                           randomRadiationMeasurementCodeItem)),
                new DicomContentItem(
                    ObservationConstants.FluoroDoseAreaProductTotal,
                    DicomRelationship.Contains,
                    new DicomMeasuredValue(randomDecimalNumber,
                                           randomRadiationMeasurementCodeItem)),
                new DicomContentItem(
                    ObservationConstants.AcquisitionDoseAreaProductTotal,
                    DicomRelationship.Contains,
                    new DicomMeasuredValue(randomDecimalNumber,
                                           randomRadiationMeasurementCodeItem)),
                new DicomContentItem(
                    ObservationConstants.TotalFluoroTime,
                    DicomRelationship.Contains,
                    new DicomMeasuredValue(randomDecimalNumber,
                                           randomRadiationMeasurementCodeItem)),
                new DicomContentItem(
                    ObservationConstants.TotalNumberOfRadiographicFrames,
                    DicomRelationship.Contains,
                    new DicomMeasuredValue(10,
                                           new DicomCodeItem("1", "UCUM", "No units"))),
                new DicomContentItem(
                    ObservationConstants.AdministeredActivity,
                    DicomRelationship.Contains,
                    new DicomMeasuredValue(randomDecimalNumber,
                                           randomRadiationMeasurementCodeItem)),
                new DicomContentItem(
                    ObservationConstants.CtDoseLengthProductTotal,
                    DicomRelationship.Contains,
                    new DicomMeasuredValue(randomDecimalNumber,
                                           randomRadiationMeasurementCodeItem)),
                new DicomContentItem(
                    ObservationConstants.TotalNumberOfIrradiationEvents,
                    DicomRelationship.Contains,
                    new DicomMeasuredValue(10,
                                           new DicomCodeItem("1", "UCUM", "No units"))),
                new DicomContentItem(
                    ObservationConstants.MeanCtdIvol,
                    DicomRelationship.Contains,
                    new DicomMeasuredValue(randomDecimalNumber,
                                           randomRadiationMeasurementCodeItem)),
                new DicomContentItem(
                    ObservationConstants.RadiopharmaceuticalAgent,
                    DicomRelationship.Contains,
                    DicomValueType.Text,
                    "Uranium"),
                new DicomContentItem(
                    ObservationConstants.Radionuclide,
                    DicomRelationship.Contains,
                    DicomValueType.Text,
                    "Uranium"),
                new DicomContentItem(
                    ObservationConstants.RadiopharmaceuticalVolume,
                    DicomRelationship.Contains,
                    new DicomMeasuredValue(randomDecimalNumber,
                                           randomRadiationMeasurementCodeItem)),
                new DicomContentItem(
                    ObservationConstants.RouteOfAdministration,
                    DicomRelationship.Contains,
                    new DicomCodeItem("needle", "random-scheme", "this is made up"))
                );

            var observations = _observationParser.Parse(
                report.Dataset,
                new ResourceReference(),
                new ResourceReference(),
                IdentifierUtility.CreateIdentifier(studyInstanceUid));

            Assert.Single(observations);

            Observation doseSummary = observations.First();

            Assert.Equal(2, doseSummary.Identifier.Count);
            Assert.Equal("urn:oid:" + studyInstanceUid,
                         doseSummary.Identifier[0].Value);
            Assert.Equal(accessionNumber,
                         doseSummary.Identifier[1].Value);
            Assert.Equal(10,
                         doseSummary.Component
                         .Count(component => component.Value is Quantity));
            Assert.Equal(2,
                         doseSummary.Component
                         .Count(component => component.Value is Integer));
            Assert.Equal(2,
                         doseSummary.Component
                         .Count(component => component.Value is FhirString));
            Assert.Equal(1,
                         doseSummary.Component
                         .Count(component => component.Value is CodeableConcept));
        }