예제 #1
0
 /// <summary>
 /// Constructor.
 /// </summary>
 /// <param name="endpoint"></param>
 /// <param name="certificate"></param>
 /// <param name="facilityType"></param>
 /// <param name="practiceSetting"></param>
 /// <param name="clientSystem"></param>
 /// <param name="productInfo"></param>
 public MhrDocumentUploadClient(string endpoint, X509Certificate2 certificate, HealthcareFacilityTypeCodes facilityType,
                                PracticeSettingTypes practiceSetting, CommonPcehrHeaderClientSystemType clientSystem, ProductInfo productInfo)
 {
     _endpoint        = endpoint;
     _certificate     = certificate;
     _facilityType    = facilityType;
     _practiceSetting = practiceSetting;
     _clientSystem    = clientSystem;
     _productInfo     = productInfo;
 }
예제 #2
0
 /// <summary>
 /// Helper method to generate the request object when submitting a new document to the PCEHR.
 /// </summary>
 /// <param name="cdaPackageContent">Byte content of the CDA package.</param>
 /// <param name="formatCode">Format code.</param>
 /// <param name="formatCodeName">Format code display name.</param>
 /// <param name="healthcareFacilityTypeCode">Healthcare facility type code.</param>
 /// <param name="practiceSetting">Practice setting code.</param>
 /// <returns>The populated request object.</returns>
 public ProvideAndRegisterDocumentSetRequestType CreateRequestForNewDocument(
     byte[] cdaPackageContent,
     string formatCode,
     string formatCodeName,
     HealthcareFacilityTypeCodes healthcareFacilityTypeCode,
     PracticeSettingTypes practiceSetting)
 {
     return(CreateRequest(
                cdaPackageContent,
                formatCode,
                formatCodeName,
                healthcareFacilityTypeCode,
                practiceSetting,
                null
                ));
 }
예제 #3
0
        /// <summary>
        /// Helper method to generate the request object.
        /// </summary>
        /// <param name="cdaPackageContent">Byte content of the CDA package.</param>
        /// <param name="formatCode">Format code.</param>
        /// <param name="formatCodeName">Format code name.</param>
        /// <param name="healthcareFacilityTypeCode">Healthcare facility type code.</param>
        /// <param name="practiceSetting">Practice setting code.</param>
        /// <param name="uuidOfDocumentToReplace">UUID of document to replace. NULL for new documents.</param>
        /// <returns>The populated request object.</returns>
        internal ProvideAndRegisterDocumentSetRequestType CreateRequest(
            byte[] cdaPackageContent,
            string formatCode,
            string formatCodeName,
            HealthcareFacilityTypeCodes healthcareFacilityTypeCode,
            PracticeSettingTypes practiceSetting,
            string uuidOfDocumentToReplace)
        {
            Validation.ValidateArgumentRequired("cdaPackageContent", cdaPackageContent);
            Validation.ValidateArgumentRequired("formatCode", formatCodeName);
            Validation.ValidateArgumentRequired("formatCodeName", formatCodeName);

            var cdaFile = GetCdaDocument(cdaPackageContent);
            var cdaDoc  = new XmlDocument();

            cdaDoc.Load(new MemoryStream(cdaFile));

            var metadata = new XdsMetadata(
                cdaDoc,
                null,
                formatCode,
                formatCodeName,
                healthcareFacilityTypeCode,
                practiceSetting,
                null,
                null,
                false,
                uuidOfDocumentToReplace
                );
            var sor = metadata.CreateSubmitObjectsRequest();

            var request = new ProvideAndRegisterDocumentSetRequestType();

            request.Document = new ProvideAndRegisterDocumentSetRequestTypeDocument[]
            {
                new ProvideAndRegisterDocumentSetRequestTypeDocument()
                {
                    id    = "DOCUMENT_SYMBOLICID_01",
                    Value = cdaPackageContent
                }
            };
            request.SubmitObjectsRequest = sor;

            return(request);
        }
 /// <summary>
 /// Helper method to generate the request object when registering a new document on the PCEHR.
 /// </summary>
 /// <param name="cdaPackageContent">Byte content of the CDA package.</param>
 /// <param name="uniqueRepositoryId">Repository ID.</param>
 /// <param name="formatCode">Format code.</param>
 /// <param name="formatCodeName">Format code display name.</param>
 /// <param name="healthcareFacilityTypeCode">Healthcare facility type code.</param>
 /// <param name="practiceSetting">Practice setting code.</param>
 /// <returns>The populated request object.</returns>
 public SubmitObjectsRequest CreateRequestForNewDocument(
     byte[] cdaPackageContent,
     string uniqueRepositoryId,
     string formatCode,
     string formatCodeName,
     HealthcareFacilityTypeCodes healthcareFacilityTypeCode,
     PracticeSettingTypes practiceSetting)
 {
     return(CreateRequest(
                cdaPackageContent,
                uniqueRepositoryId,
                formatCode,
                formatCodeName,
                healthcareFacilityTypeCode,
                practiceSetting,
                null
                ));
 }
예제 #5
0
        /// <summary>
        /// Helper method to generate the request object when submitting a replacement document.
        /// </summary>
        /// <param name="cdaPackageContent">Byte content of the CDA package.</param>
        /// <param name="formatCode">Format code.</param>
        /// <param name="formatCodeName">Format code display name.</param>
        /// <param name="healthcareFacilityTypeCode">Healthcare facility type code.</param>
        /// <param name="practiceSetting">Practice setting code.</param>
        /// <param name="uuidOfDocumentToReplace">UUID of document to replace.</param>
        /// <returns>The populated request object.</returns>
        public ProvideAndRegisterDocumentSetRequestType CreateRequestForReplacement(
            byte[] cdaPackageContent,
            string formatCode,
            string formatCodeName,
            HealthcareFacilityTypeCodes healthcareFacilityTypeCode,
            PracticeSettingTypes practiceSetting,
            string uuidOfDocumentToReplace)
        {
            Validation.ValidateArgumentRequired("uuidOfDocumentToReplace", uuidOfDocumentToReplace);

            return(CreateRequest(
                       cdaPackageContent,
                       formatCode,
                       formatCodeName,
                       healthcareFacilityTypeCode,
                       practiceSetting,
                       uuidOfDocumentToReplace
                       ));
        }
        /// <summary>
        /// Helper method to generate the request object when registering a replacement document on the PCEHR.
        /// </summary>
        /// <param name="cdaPackageContent">Byte content of the CDA package.</param>
        /// <param name="uniqueRepositoryId">Repository ID.</param>
        /// <param name="formatCode">Format code.</param>
        /// <param name="formatCodeName">Format code display name.</param>
        /// <param name="healthcareFacilityTypeCode">Healthcare facility type code.</param>
        /// <param name="practiceSetting">Practice setting code.</param>
        /// <param name="uuidOfDocumentToReplace">UUID of document to replace.</param>
        /// <returns>The populated request object.</returns>
        public SubmitObjectsRequest CreateRequestForReplacement(
            byte[] cdaPackageContent,
            string uniqueRepositoryId,
            string formatCode,
            string formatCodeName,
            HealthcareFacilityTypeCodes healthcareFacilityTypeCode,
            PracticeSettingTypes practiceSetting,
            string uuidOfDocumentToReplace)
        {
            Validation.ValidateArgumentRequired("uuidOfDocumentToReplace", uuidOfDocumentToReplace);

            return(CreateRequest(
                       cdaPackageContent,
                       uniqueRepositoryId,
                       formatCode,
                       formatCodeName,
                       healthcareFacilityTypeCode,
                       practiceSetting,
                       uuidOfDocumentToReplace
                       ));
        }
        /// <summary>
        /// Helper method to generate the request object.
        /// </summary>
        /// <param name="cdaPackageContent">Byte content of the CDA package.</param>
        /// /// <param name="uniqueRepositoryId">Repository ID.</param>
        /// <param name="formatCode">Format code.</param>
        /// <param name="formatCodeName">Format code name.</param>
        /// <param name="healthcareFacilityTypeCode">Healthcare facility type code.</param>
        /// <param name="practiceSetting">Practice setting code.</param>
        /// <param name="uuidOfDocumentToReplace">UUID of document to replace. NULL for new documents.</param>
        /// <returns>The populated request object.</returns>
        internal SubmitObjectsRequest CreateRequest(
            byte[] cdaPackageContent,
            string uniqueRepositoryId,
            string formatCode,
            string formatCodeName,
            HealthcareFacilityTypeCodes healthcareFacilityTypeCode,
            PracticeSettingTypes practiceSetting,
            string uuidOfDocumentToReplace)
        {
            Validation.ValidateArgumentRequired("cdaPackageContent", cdaPackageContent);
            Validation.ValidateArgumentRequired("uniqueRepositoryId", uniqueRepositoryId);
            Validation.ValidateArgumentRequired("formatCode", formatCodeName);
            Validation.ValidateArgumentRequired("formatCodeName", formatCodeName);

            var cdaFile = GetCdaDocument(cdaPackageContent);
            var cdaDoc  = new XmlDocument();

            cdaDoc.Load(new MemoryStream(cdaFile));

            var metadata = new XdsMetadata(
                cdaDoc,
                uniqueRepositoryId,
                formatCode,
                formatCodeName,
                healthcareFacilityTypeCode,
                practiceSetting,
                cdaPackageContent.Length,
                CalculateSHA1(cdaPackageContent),
                true,
                uuidOfDocumentToReplace
                );

            var sor           = metadata.CreateSubmitObjectsRequest();
            var serializedSor = sor.SerializeToXml();

            return(serializedSor.Deserialize <SubmitObjectsRequest>());
        }
        public XdsMetadata(
            XmlDocument cdaDocument,
            string repositoryId,

            // FormatCodes formatCode,
            string formatCode,
            string formatCodeName,

            HealthcareFacilityTypeCodes healthcareFacilityTypeCode,
            PracticeSettingTypes practiceSetting,
            int?size,
            string hash,
            bool isUpdateMetadata,
            string uuidOfDocumentToReplace)
        {
            this.formatCode     = formatCode;
            this.formatCodeName = formatCodeName;

            this.healthcareFacilityTypeCode = healthcareFacilityTypeCode;
            this.practiceSetting            = practiceSetting;
            this.isUpdateMetadata           = isUpdateMetadata;

            this.uuidOfDocumentToReplace = uuidOfDocumentToReplace;

            if (isUpdateMetadata)
            {
                this.repositoryId = repositoryId;
                this.size         = size.Value;
                this.hash         = hash;
            }

            var xnm = new XmlNamespaceManager(cdaDocument.NameTable);

            xnm.AddNamespace("cda", "urn:hl7-org:v3");
            xnm.AddNamespace("ext", "http://ns.electronichealth.net.au/Ci/Cda/Extensions/3.0");

            // Document Type
            documentTypeCode = CheckNullValue(cdaDocument.SelectSingleNode("/cda:ClinicalDocument/cda:code/@code", xnm));
            var classCode = GetClassCodeEnum(documentTypeCode);

            documentTypeCodeSystemName = CheckNullValue(cdaDocument.SelectSingleNode("/cda:ClinicalDocument/cda:code/@codeSystemName", xnm));
            documentTypeDisplayName    = classCode.GetAttributeValue <CodedValueAttribute, string>(a => a.AlternateName);
            // 14/10 Updated Spec says we should use the AlternateName for both Type and Class Code
            documentClassCodeDisplayName = classCode.GetAttributeValue <CodedValueAttribute, string>(a => a.AlternateName);

            documentTypeCode_cl07           = documentTypeCode;
            documentTypeDisplayName_cl07    = documentTypeDisplayName;
            documentTypeCodeSystemName_cl07 = documentTypeCodeSystemName;

            // The exception is for Advance Care Information = which has Advance Care Planning Document/Goals of Care Document subtypes
            if (documentTypeCode == "100.16975")
            {
                // Get document desc and code from the body
                documentTypeCode_cl07        = CheckNullValue(cdaDocument.SelectSingleNode("/cda:ClinicalDocument/cda:component/cda:structuredBody/cda:component/cda:section/cda:entry/cda:act/cda:reference/cda:externalDocument/cda:code/@code", xnm));
                documentTypeDisplayName_cl07 = CheckNullValue(cdaDocument.SelectSingleNode("/cda:ClinicalDocument/cda:component/cda:structuredBody/cda:component/cda:section/cda:entry/cda:act/cda:reference/cda:externalDocument/cda:code/@displayName", xnm));

                if (documentTypeCode_cl07 == "" || documentTypeDisplayName_cl07 == "")
                {
                    throw new ArgumentException("The Advance Care Information does not reference an externalDocument.");
                }
            }

            // Get time stuff
            effectiveTime = CheckNullValue(cdaDocument.SelectSingleNode("/cda:ClinicalDocument/cda:effectiveTime/@value", xnm));
            effectiveTime = ConvertTimeToUtc(effectiveTime);

            // Set service start and stop time
            if (documentTypeCode == "51852-2")
            {
                // If document is Specialist Letter
                serviceStartTime = effectiveTime;
                serviceStopTime  = effectiveTime;
            }
            else if (documentTypeCode == "100.16764")
            {
                // If document is PCEHR Prescription Record
                var authorTime = CheckNullValue(cdaDocument.SelectSingleNode("/cda:ClinicalDocument/cda:author/cda:time/@value", xnm));

                serviceStartTime = ConvertTimeToUtc(authorTime);
                serviceStopTime  = serviceStartTime;
            }
            else if (documentTypeCode == "100.16765")
            {
                // If document is PCEHR Dispense Record
                var supplyTime = CheckNullValue(cdaDocument.SelectSingleNode("/cda:ClinicalDocument/cda:component/cda:structuredBody/" +
                                                                             "cda:component/cda:section[cda:code/@code='102.16210' and cda:code/@codeSystem='1.2.36.1.2001.1001.101']/cda:entry/" +
                                                                             "cda:substanceAdministration/cda:entryRelationship/cda:supply/cda:effectiveTime/@value", xnm));

                serviceStartTime = ConvertTimeToUtc(supplyTime);
                serviceStopTime  = serviceStartTime;
            }
            else if (documentTypeCode == "100.32001")
            {
                // R5: If document is Pathology Report - Could contain multiple records
                // Need to get LATEST Date - if multiple returned

                XmlNodeList nList = cdaDocument.SelectNodes("/cda:ClinicalDocument/cda:component/cda:structuredBody/cda:component" +
                                                            "/cda:section[cda:code/@code='101.20018']/cda:component" +
                                                            "/cda:section[cda:code/@code='102.16144']/cda:entry/cda:observation" +
                                                            "/cda:entryRelationship/cda:observation[cda:code/@code='102.16156']" +
                                                            "/cda:effectiveTime/@value", xnm);
                var firstCollectionDateTime = "";
                var lastCollectionDateTime  = "";
                if (nList.Count == 1)
                {
                    firstCollectionDateTime = nList[0].Value;
                    lastCollectionDateTime  = nList[0].Value;
                }
                else if (nList.Count > 1)
                {
                    string[] sDates = new string[nList.Count];
                    for (int i = 0; i < nList.Count; i++)
                    {
                        sDates[i] = nList[i].Value;
                    }
                    Array.Sort(sDates);
                    //Get last entry
                    firstCollectionDateTime = sDates[0];
                    lastCollectionDateTime  = sDates[nList.Count - 1];
                }

                serviceStartTime = ConvertTimeToUtc(firstCollectionDateTime);
                serviceStopTime  = ConvertTimeToUtc(lastCollectionDateTime);
            }
            else if (documentTypeCode == "100.16957")
            {
                // R5: If document is Diagnostic Imaging - Could contain multiple records
                // Need to get LATEST Date - if multiple returned
                XmlNodeList nList = cdaDocument.SelectNodes("/cda:ClinicalDocument/cda:component/cda:structuredBody/cda:component" +
                                                            "/cda:section/cda:component/cda:section/cda:entry/cda:observation" +
                                                            "/cda:entryRelationship/cda:act[cda:code/@code='102.16511']/cda:entryRelationship" +
                                                            "/cda:observation/cda:effectiveTime/@value", xnm);
                var firstImagingDateTime = "";
                var lastImagingDateTime  = "";
                if (nList.Count == 1)
                {
                    firstImagingDateTime = nList[0].Value;
                    lastImagingDateTime  = nList[0].Value;
                }
                else if (nList.Count > 1)
                {
                    string[] sDates = new string[nList.Count];
                    for (int i = 0; i < nList.Count; i++)
                    {
                        sDates[i] = nList[i].Value;
                    }
                    Array.Sort(sDates);
                    //Get last entry
                    firstImagingDateTime = sDates[0];
                    lastImagingDateTime  = sDates[nList.Count - 1];
                }

                serviceStartTime = ConvertTimeToUtc(firstImagingDateTime);
                serviceStopTime  = ConvertTimeToUtc(lastImagingDateTime);
            }
            else if (documentTypeCode == "100.16975")
            {
                // Advance Care Planning Document
                var authorTime = CheckNullValue(cdaDocument.SelectSingleNode("/cda:ClinicalDocument/cda:component/cda:structuredBody/cda:component/cda:section[cda:code/@code='101.16973']/cda:entry/cda:act/cda:author/cda:time/@value", xnm));
                serviceStartTime = ConvertTimeToUtc(authorTime);
                serviceStopTime  = serviceStartTime;
            }
            else
            {
                // For other document types set service start and stop time to encompassingEncounter/effectiveTime if available
                string startTime1 = CheckNullValue(cdaDocument.SelectSingleNode("/cda:ClinicalDocument/cda:componentOf/cda:encompassingEncounter/cda:effectiveTime/@value", xnm));
                string startTime2 = CheckNullValue(cdaDocument.SelectSingleNode("/cda:ClinicalDocument/cda:componentOf/cda:encompassingEncounter/cda:effectiveTime/cda:low/@value", xnm));
                string stopTime   = CheckNullValue(cdaDocument.SelectSingleNode("/cda:ClinicalDocument/cda:componentOf/cda:encompassingEncounter/cda:effectiveTime/cda:high/@value", xnm));

                serviceStartTime = !string.IsNullOrEmpty(startTime1)
                                       ? startTime1
                                       : !string.IsNullOrEmpty(startTime2)
                                             ? startTime2
                                             : effectiveTime;
                serviceStartTime = ConvertTimeToUtc(serviceStartTime);

                serviceStopTime = !string.IsNullOrEmpty(stopTime)
                                      ? stopTime
                                      : !string.IsNullOrEmpty(startTime1)
                                            ? startTime1
                                            : effectiveTime;

                serviceStopTime = ConvertTimeToUtc(serviceStopTime);
            }

            // As per DEXS-T123, serviceStartTime and serviceStopTime must be at least 8 chars long eg UTC formats: YYYYMMDD, YYYYMMDDhhmm, and YYYYMMDDhhmmss
            if (serviceStartTime.Length < 8)
            {
                throw new ArgumentException("The serviceStartTime retrieved from the CDA document must be at least 8 digits long.");
            }
            if (serviceStopTime.Length < 8)
            {
                throw new ArgumentException("The serviceStopTime retrieved from the CDA document must be at least 8 digits long.");
            }

            // Get Data
            languageCode = CheckNullValue(cdaDocument.SelectSingleNode("/cda:ClinicalDocument/cda:languageCode/@code", xnm));
            //Added as some documents do not include it. (4/2/15)
            if (String.IsNullOrEmpty(languageCode))
            {
                languageCode = "en-AU";
            }
            cdaTitle      = CheckNullText(cdaDocument.SelectSingleNode("/cda:ClinicalDocument/cda:title", xnm));
            documentTitle = !string.IsNullOrEmpty(cdaTitle) ? cdaTitle : documentTypeDisplayName;

            // Process document ID
            XdsMetadataHelper.IdType?idType;
            documentId          = CheckNullValue(cdaDocument.SelectSingleNode("/cda:ClinicalDocument/cda:id/@root", xnm));
            documentIdExtension = CheckNullValue(cdaDocument.SelectSingleNode("/cda:ClinicalDocument/cda:id/@extension", xnm));
            cdaDocumentIdOid    = XdsMetadataHelper.UuidToOid(documentId, out idType);
            if (idType == null)
            {
                throw new ArgumentException("The CDA document must contain an ID which is either a UUID or an OID.");
            }
            else if (idType == XdsMetadataHelper.IdType.Oid && !string.IsNullOrEmpty(documentIdExtension))
            {
                cdaDocumentIdOid = cdaDocumentIdOid + "^" + documentIdExtension;
            }

            XmlNode authorNode = null;

            if (cdaDocument.SelectSingleNode("/cda:ClinicalDocument/cda:author/cda:assignedAuthor/cda:assignedPerson", xnm) != null)
            {
                authorNode = cdaDocument.SelectSingleNode("/cda:ClinicalDocument/cda:author/cda:assignedAuthor", xnm);
            }

            // If document author doesn't exist, and document is pathology
            if (authorNode == null && documentTypeCode == "100.32001")
            {
                authorNode = cdaDocument.SelectSingleNode("/cda:ClinicalDocument/cda:component/cda:structuredBody/cda:component/cda:section[cda:code/@code='101.20018']/cda:author/cda:assignedAuthor", xnm);
            }

            bool authorDevice = false;

            if (cdaDocument.SelectSingleNode("/cda:ClinicalDocument/cda:author/cda:assignedAuthor/cda:assignedAuthoringDevice", xnm) != null)
            {
                authorNode   = authorNode = cdaDocument.SelectSingleNode("/cda:ClinicalDocument/cda:author/cda:assignedAuthor", xnm);
                authorDevice = true;
            }

            if (authorNode == null && authorDevice == false)
            {
                throw new ArgumentException("Unable to determine document author from CDA document");
            }

            // Current Author format
            string authorOrgIdCurr   = CheckNullValue(authorNode.SelectSingleNode("cda:assignedPerson/ext:asEmployment/ext:employerOrganization/cda:asOrganizationPartOf/cda:wholeOrganization/ext:asEntityIdentifier[@classCode='IDENT']/ext:id[@assigningAuthorityName='HPI-O']/@root", xnm));
            string authorOrgNameCurr = CheckNullText(authorNode.SelectSingleNode("cda:assignedPerson/ext:asEmployment/ext:employerOrganization/cda:asOrganizationPartOf/cda:wholeOrganization/cda:name", xnm));

            // Check Custodian if Author has no Org [Dont need code - as it would fail PCEHR validation for the NCAP 5 documents]
            string authorOrgIdCust   = CheckNullValue(cdaDocument.SelectSingleNode("/cda:ClinicalDocument/cda:custodian/cda:assignedCustodian/cda:representedCustodianOrganization/ext:asEntityIdentifier[@classCode='IDENT']/ext:id[@assigningAuthorityName='HPI-O']/@root", xnm));
            string authorOrgNameCust = CheckNullText(cdaDocument.SelectSingleNode("/cda:ClinicalDocument/cda:custodian/cda:assignedCustodian/cda:representedCustodianOrganization/cda:name", xnm));

            // Check Health Care Facility in Component Of if Author has no Org
            string authorOrgIdHCF   = CheckNullValue(cdaDocument.SelectSingleNode("/cda:ClinicalDocument/cda:componentOf/cda:encompassingEncounter/cda:location/cda:healthCareFacility/cda:serviceProviderOrganization/cda:asOrganizationPartOf/cda:wholeOrganization/ext:asEntityIdentifier[@classCode='IDENT']/ext:id[@assigningAuthorityName='HPI-O']/@root", xnm));
            string authorOrgNameHCF = CheckNullText(cdaDocument.SelectSingleNode("/cda:ClinicalDocument/cda:componentOf/cda:encompassingEncounter/cda:location/cda:healthCareFacility/cda:serviceProviderOrganization/cda:asOrganizationPartOf/cda:wholeOrganization/cda:name", xnm));

            authorQualifiedOrgId = (authorOrgIdCurr != "" ? authorOrgIdCurr : (authorOrgIdCust != "" ? authorOrgIdCust : authorOrgIdHCF));
            if (String.IsNullOrEmpty(authorOrgName))
            {
                authorOrgName = (authorOrgNameCurr != "" ? authorOrgNameCurr : (authorOrgNameCust != "" ? authorOrgNameCust : authorOrgNameHCF));
            }

            // AUTHOR
            authorQualifiedId = CheckNullValue(authorNode.SelectSingleNode("cda:assignedPerson/ext:asEntityIdentifier[@classCode='IDENT']/ext:id[@assigningAuthorityName='HPI-I']/@root", xnm));
            if (string.IsNullOrEmpty(authorQualifiedId))
            {
                authorQualifiedId = CheckNullValue(authorNode.SelectSingleNode("cda:assignedAuthoringDevice/ext:asEntityIdentifier[@classCode='IDENT']/ext:id[@assigningAuthorityName='PAI-D']/@root", xnm));
            }
            if (string.IsNullOrEmpty(authorQualifiedId))
            {
                authorQualifiedId = CheckNullValue(cdaDocument.SelectSingleNode("cda:assignedPerson/ext:asEntityIdentifier[@classCode='IDENT']/ext:id[@assigningAuthorityName='PAI-D']/@root", xnm));
            }
            if (string.IsNullOrEmpty(authorQualifiedId))
            {
                authorQualifiedId          = CheckNullValue(authorNode.SelectSingleNode("cda:assignedPerson/ext:asEntityIdentifier[@classCode='IDENT']/ext:id/@root", xnm));
                authorQualifiedIdExtension = CheckNullValue(authorNode.SelectSingleNode("cda:assignedPerson/ext:asEntityIdentifier[@classCode='IDENT']/ext:id/@extension", xnm));
            }
            if (string.IsNullOrEmpty(authorQualifiedId))
            {
                throw new ArgumentException("The CDA document must contain an author identifier.");
            }
            if (string.IsNullOrEmpty(authorFamily))
            {
                authorFamily = CheckNullText(authorNode.SelectSingleNode("cda:assignedPerson/cda:name/cda:family", xnm));
            }
            authorGiven  = CheckNullText(authorNode.SelectSingleNode("cda:assignedPerson/cda:name/cda:given", xnm));
            authorPrefix = CheckNullText(authorNode.SelectSingleNode("cda:assignedPerson/cda:name/cda:prefix", xnm));
            authorSuffix = CheckNullText(authorNode.SelectSingleNode("cda:assignedPerson/cda:name/cda:suffix", xnm));

            // Author specialty
            authorSpecialty = CheckNullValue(authorNode.SelectSingleNode("cda:code/@displayName", xnm));

            // HI numbers (for header)
            hpioNumber = authorQualifiedOrgId.Replace("1.2.36.1.2001.1003.0.", "");
            ihiNumber  = CheckNullValue(cdaDocument.SelectSingleNode("/cda:ClinicalDocument/cda:recordTarget/cda:patientRole/cda:patient/ext:asEntityIdentifier[@classCode='IDENT']/ext:id[@assigningAuthorityName='IHI']/@root", xnm)).Replace("1.2.36.1.2001.1003.0.", "");

            // Formatted Ids
            cxFormattedPatientId = ihiNumber + "^^^&1.2.36.1.2001.1003.0&ISO";

            // Correct format (3 hats before HPII)
            if (string.IsNullOrEmpty(authorQualifiedIdExtension))
            {
                xcnFormattedAuthor = string.Format("^{0}^{1}^^^{2}^^^&{3}&ISO", authorFamily, authorGiven, authorPrefix, authorQualifiedId);
            }
            else
            {
                xcnFormattedAuthor = string.Format("^{0}^{1}^^^{2}^^^{3}&{4}&ISO", authorFamily, authorGiven, authorPrefix, authorQualifiedId, authorQualifiedIdExtension);
            }
            xonFormattedOrganisation = string.Format("{0}^^^^^^^^^{1}", authorOrgName, authorQualifiedOrgId);
        }