///<summary> ///<para>if ValueSetOID=2.16.840.1.113883.3.526.3.1254, then Medication, Administered not done: Medical/Patient/System Reason (flu vaccine med)</para> ///<para>if ValueSetOID=2.16.840.1.113883.3.526.3.402, then Procedure, Performed not done: Medical/Patient/System Reason (flu vaccine proc)</para> ///<para>if ValueSetOID=2.16.840.1.113883.3.600.1.462, then Procedure, Performed not done: Medical or Other reason not done (current meds documented proc)</para> ///<para>if ValueSetOID=2.16.840.1.113883.3.600.1.681, then Physical Exam, Performed not done: Medical or Other reason not done/Patient Reason Refused (vitalsign exam)</para> ///<para>if ValueSetOID=2.16.840.1.113883.3.526.3.1278, then Risk Category Assessment not done: Medical Reason (tobacco assessment)</para> ///<para>Then use the negationInd="true" attribute to indicate that it was not performed</para></summary> private static void GenerateNotPerfEntry(EhrCqmNotPerf npCur) { _isWriterW=false; Start("entry","typeCode","DRIV"); if(npCur.ValueSetOID=="2.16.840.1.113883.3.526.3.1254") {//flu vaccine medication administered not done Start("act","classCode","ACT","moodCode","EVN"); _x.WriteComment("Medication Administered Template"); TemplateId("2.16.840.1.113883.10.20.24.3.42"); StartAndEnd("id","root",_strOIDInternalCQMRoot,"extension",CqmItemAbbreviation.NotPerf.ToString()+npCur.EhrCqmNotPerfNum.ToString()); StartAndEnd("code","code","416118004","displayName","Administration","codeSystem",strCodeSystemSnomed,"codeSystemName",strCodeSystemNameSnomed); StartAndEnd("statusCode","code","completed"); StartAndEnd("effectiveTime","nullFlavor","NI"); Start("entryRelationship","typeCode","COMP"); Start("substanceAdministration","classCode","SBADM","moodCode","EVN","negationInd","true"); _x.WriteComment("Medication Activity Template"); TemplateId("2.16.840.1.113883.10.20.22.4.16"); StartAndEnd("id","root",_strOIDInternalCQMRoot,"extension",CqmItemAbbreviation.NotPerf.ToString()+npCur.EhrCqmNotPerfNum.ToString()+npCur.PatNum.ToString()); _x.WriteElementString("text","Medication Administered, not done: "+npCur.ValueSetName); StartAndEnd("statusCode","code","completed"); Start("effectiveTime"); DateElement("low",npCur.DateEntry); End("effectiveTime"); Start("consumable"); Start("manufacturedProduct","classCode","MANU"); _x.WriteComment("Medication Information Template"); TemplateId("2.16.840.1.113883.10.20.22.4.23"); StartAndEnd("id","root",_strOIDInternalCQMRoot,"extension",CqmItemAbbreviation.NotPerf.ToString()+npCur.EhrCqmNotPerfNum.ToString()+npCur.CodeValue.ToString()); Start("manufacturedMaterial"); Start("code","code",npCur.CodeValue,"displayName",npCur.Description,"codeSystem",npCur.CodeSystemOID,"codeSystemName",npCur.CodeSystemName); _x.WriteAttributeString("sdtc","valueSet",null,npCur.ValueSetOID); End("code"); End("manufacturedMaterial"); End("manufacturedProduct"); End("consumable"); Start("entryRelationship","typeCode","RSON"); Start("observation","classCode","OBS","moodCode","EVN"); _x.WriteComment("Reason Observation Template"); TemplateId("2.16.840.1.113883.10.20.24.3.88"); StartAndEnd("code","code","410666004","displayName","reason","codeSystem",strCodeSystemSnomed,"codeSystemName",strCodeSystemNameSnomed); Start("value"); _x.WriteAttributeString("xsi","type",null,"CD"); Attribs("code",npCur.CodeValueReason,"displayName",npCur.DescriptionReason,"codeSystem",npCur.CodeSystemOIDReason,"codeSystemName",npCur.CodeSystemNameReason); _x.WriteAttributeString("sdtc","valueSet",null,npCur.ValueSetOIDReason); End("value"); End("observation"); End("entryRelationship"); End("substanceAdministration"); End("entryRelationship"); End("act"); } else if(npCur.ValueSetOID=="2.16.840.1.113883.3.526.3.402" || npCur.ValueSetOID=="2.16.840.1.113883.3.600.1.462") {//flu vaccine proc or current meds documented proc performed not done Start("procedure","classCode","PROC","moodCode","EVN","negationInd","true"); _x.WriteComment("Procedure Activity Procedure Template"); TemplateId("2.16.840.1.113883.10.20.22.4.14"); _x.WriteComment("Procedure Performed Template"); TemplateId("2.16.840.1.113883.10.20.24.3.64"); StartAndEnd("id","root",_strOIDInternalCQMRoot,"extension",CqmItemAbbreviation.Proc.ToString()+npCur.EhrCqmNotPerfNum.ToString()); Start("code","code",npCur.CodeValue,"displayName",npCur.Description,"codeSystem",npCur.CodeSystemOID,"codeSystemName",npCur.CodeSystemName); _x.WriteAttributeString("sdtc","valueSet",null,npCur.ValueSetOID); End("code"); _x.WriteElementString("text","Procedure Performed, not done: "+npCur.Description); StartAndEnd("statusCode","code","completed"); Start("effectiveTime"); DateElement("low",npCur.DateEntry); DateElement("high",npCur.DateEntry); End("effectiveTime"); Start("entryRelationship","typeCode","RSON"); Start("observation","classCode","OBS","moodCode","EVN"); _x.WriteComment("Reason Template"); TemplateId("2.16.840.1.113883.10.20.24.3.88"); StartAndEnd("code","code","410666004","displayName","reason","codeSystem",strCodeSystemSnomed,"codeSystemName",strCodeSystemNameSnomed); StartAndEnd("statusCode","code","completed"); Start("effectiveTime"); DateElement("low",npCur.DateEntry); End("effectiveTime"); Start("value"); _x.WriteAttributeString("xsi","type",null,"CD"); Attribs("code",npCur.CodeValueReason,"displayName",npCur.DescriptionReason,"codeSystem",npCur.CodeSystemOIDReason,"codeSystemName",npCur.CodeSystemNameReason); _x.WriteAttributeString("sdtc","valueSet",null,npCur.ValueSetOIDReason); End("value"); End("observation"); End("entryRelationship"); End("procedure"); } else if(npCur.ValueSetOID=="2.16.840.1.113883.3.600.1.681") {//BMI exam performed not done _x.WriteComment("Physical Exam Finding"); Start("observation","classCode","OBS","moodCode","EVN","negationInd","true"); _x.WriteComment("Result Observation Template"); TemplateId("2.16.840.1.113883.10.20.22.4.2"); _x.WriteComment("Physical Exam Finding Template"); TemplateId("2.16.840.1.113883.10.20.24.3.57"); StartAndEnd("id","root",_strOIDInternalCQMRoot,"extension",CqmItemAbbreviation.Vital.ToString()+npCur.EhrCqmNotPerfNum.ToString()); Start("code","code","39156-5","displayName","Body mass index (BMI) [Ratio]","codeSystem",strCodeSystemLoinc,"codeSystemName",strCodeSystemNameLoinc); _x.WriteAttributeString("sdtc","valueSet",null,"2.16.840.1.113883.3.600.1.681"); End("code"); _x.WriteElementString("text","Physical Exam Performed, not done: BMI LOINC Value"); StartAndEnd("statusCode","code","completed"); Start("effectiveTime"); DateElement("low",npCur.DateEntry); End("effectiveTime"); StartAndEnd("value","nullFlavor","NI"); Start("entryRelationship","typeCode","RSON"); Start("observation","classCode","OBS","moodCode","EVN"); _x.WriteComment("Reason Template"); TemplateId("2.16.840.1.113883.10.20.24.3.88"); StartAndEnd("code","code","410666004","displayName","reason","codeSystem",strCodeSystemSnomed,"codeSystemName",strCodeSystemNameSnomed); StartAndEnd("statusCode","code","completed"); Start("effectiveTime"); DateElement("low",npCur.DateEntry); End("effectiveTime"); Start("value"); _x.WriteAttributeString("xsi","type",null,"CD"); Attribs("code",npCur.CodeValueReason,"displayName",npCur.DescriptionReason,"codeSystem",npCur.CodeSystemOIDReason,"codeSystemName",npCur.CodeSystemNameReason); _x.WriteAttributeString("sdtc","valueSet",null,npCur.ValueSetOIDReason); End("value"); End("observation"); End("entryRelationship"); End("observation"); } else {//must be a tobacco assessment not done Start("observation","classCode","OBS","moodCode","EVN","negationInd","true"); _x.WriteComment("Tobacco Use Template"); TemplateId("2.16.840.1.113883.10.20.22.4.85"); StartAndEnd("id","root",_strOIDInternalCQMRoot,"extension",CqmItemAbbreviation.MeasEvn.ToString()+npCur.EhrCqmNotPerfNum.ToString()); StartAndEnd("code","code","ASSERTION","displayName","Assertion","codeSystem","2.16.840.1.113883.5.4","codeSystemName","ActCode"); _x.WriteElementString("text","Risk Category Assessment, not done: "+npCur.Description); StartAndEnd("statusCode","code","completed"); Start("effectiveTime"); DateElement("low",npCur.DateEntry); End("effectiveTime"); Start("value"); _x.WriteAttributeString("xsi","type",null,"CD"); Attribs("code",npCur.CodeValue,"displayName",npCur.Description,"codeSystem",npCur.CodeSystemOID,"codeSystemName",npCur.CodeSystemName); _x.WriteAttributeString("sdtc","valueSet",null,npCur.ValueSetOID); End("value"); Start("entryRelationship","typeCode","RSON"); Start("observation","classCode","OBS","moodCode","EVN"); _x.WriteComment("Reason Template"); TemplateId("2.16.840.1.113883.10.20.24.3.88"); StartAndEnd("code","code","410666004","displayName","reason","codeSystem",strCodeSystemSnomed,"codeSystemName",strCodeSystemNameSnomed); StartAndEnd("statusCode","code","completed"); Start("effectiveTime"); DateElement("low",npCur.DateEntry); End("effectiveTime"); Start("value"); _x.WriteAttributeString("xsi","type",null,"CD"); Attribs("code",npCur.CodeValueReason,"displayName",npCur.DescriptionReason,"codeSystem",npCur.CodeSystemOIDReason,"codeSystemName",npCur.CodeSystemNameReason); _x.WriteAttributeString("sdtc","valueSet",null,npCur.ValueSetOIDReason); End("value"); End("observation"); End("entryRelationship"); End("observation"); } End("entry"); _isWriterW=true; }
///<summary>Get all NotPerformed items that belong to one of the ValueSetOIDs in listItemOIDs, with valid reasons that belong to one of the ValueSetOIDs in listReasonOIDs, that were entered between dateStart and dateStop. For QRDA reporting, the resulting list must include the item not performed, a code for 'reason', and the code for the specific reason. Example: If not administering a flu vaccine, you would have the code not being done (like CVX 141 "Influenza, seasonal, injectable"), the code for 'reason' (like SNOMEDCT 281000124100 "Patient reason for exclusion from performance measure (observable entity)"), and the code for the specific reason (like SNOMEDCT 105480006 "Refusal of treatment by patient (situation)"). Not fun.</summary> private static Dictionary<long,List<EhrCqmNotPerf>> GetNotPerformeds(List<long> listPatNums,List<string> listValueSetOIDs,List<string> listReasonOIDs,DateTime dateStart,DateTime dateEnd) { Dictionary<long,List<EhrCqmNotPerf>> retval=new Dictionary<long,List<EhrCqmNotPerf>>(); //if no patients, return a new empty dictionary if(listPatNums!=null && listPatNums.Count==0) { return retval; } List<EhrCode> listItems=EhrCodes.GetForValueSetOIDs(listValueSetOIDs,false); List<EhrCode> listReasons=EhrCodes.GetForValueSetOIDs(listReasonOIDs,false); string itemCodes=""; string reasonCodes=""; for(int i=0;i<listItems.Count;i++) { if(i>0) { itemCodes+=","; } itemCodes+=listItems[i].CodeValue; } for(int i=0;i<listReasons.Count;i++) { if(i>0) { reasonCodes+=","; } reasonCodes+=listReasons[i].CodeValue; } //Reasons not done come from these value sets for our 9 CQMs: //Medical Reason Grouping Value Set 2.16.840.1.113883.3.526.3.1007 //Patient Reason Grouping Value Set 2.16.840.1.113883.3.526.3.1008 //System Reason Grouping Value Set 2.16.840.1.113883.3.526.3.1009 //Patient Reason Refused SNOMED-CT Value Set 2.16.840.1.113883.3.600.1.1503 (this is a sub-set of Patient Reason ...1008 above) //Medical or Other reason not done SNOMED-CT Value Set 2.16.840.1.113883.3.600.1.1502 (this is a sub-set of Medical Reason ...1007 above) string command="SELECT ehrnotperformed.*, " +"COALESCE(snomed.Description,loinc.NameLongCommon,cpt.Description,cvx.Description,'') AS Description, " +"COALESCE(sReason.Description,'') AS DescriptionReason " +"FROM ehrnotperformed " +"LEFT JOIN cpt ON cpt.CptCode=ehrnotperformed.CodeValue AND ehrnotperformed.CodeSystem='CPT' " +"LEFT JOIN cvx ON cvx.CvxCode=ehrnotperformed.CodeValue AND ehrnotperformed.CodeSystem='CVX' " +"LEFT JOIN loinc ON loinc.LoincCode=ehrnotperformed.CodeValue AND ehrnotperformed.CodeSystem='LOINC' " +"LEFT JOIN snomed ON snomed.SnomedCode=ehrnotperformed.CodeValue AND ehrnotperformed.CodeSystem='SNOMEDCT' " +"LEFT JOIN snomed sReason ON sReason.SnomedCode=ehrnotperformed.CodeValueReason AND ehrnotperformed.CodeSystemReason='SNOMEDCT' " +"WHERE ehrnotperformed.DateEntry BETWEEN "+POut.Date(dateStart)+" AND "+POut.Date(dateEnd)+" "; if(listPatNums!=null && listPatNums.Count>0) { command+="AND ehrnotperformed.PatNum IN("+string.Join(",",listPatNums)+") "; } if(itemCodes!="") { command+="AND ehrnotperformed.CodeValue IN("+itemCodes+") "; } if(reasonCodes!="") { command+="AND ehrnotperformed.CodeValueReason IN("+reasonCodes+") "; } command+="GROUP BY ehrnotperformed.EhrNotPerformedNum "//just in case a code was in one of the code system tables more than once, should never happen +"ORDER BY ehrnotperformed.PatNum,ehrnotperformed.DateEntry DESC"; DataTable tableNotPerfs=Db.GetTable(command); if(tableNotPerfs.Rows.Count==0) { return retval; } Dictionary<long,EhrCode> dictItemNumEhrCode=new Dictionary<long,EhrCode>(); Dictionary<long,EhrCode> dictReasonNumEhrCode=new Dictionary<long,EhrCode>(); //loop through items and remove if not in valid value set or if reason is not in valid reason value set //link the item to the EhrCode object for both the item code and the reason code using dictionaries for retrieving required data for QRDA reports for(int i=tableNotPerfs.Rows.Count-1;i>-1;i--) { for(int j=0;j<listItems.Count;j++) { if(tableNotPerfs.Rows[i]["CodeValue"].ToString()==listItems[j].CodeValue && tableNotPerfs.Rows[i]["CodeSystem"].ToString()==listItems[j].CodeSystem) { dictItemNumEhrCode.Add(PIn.Long(tableNotPerfs.Rows[i]["EhrNotPerformedNum"].ToString()),listItems[j]); break; } } for(int j=0;j<listReasons.Count;j++) { if(tableNotPerfs.Rows[i]["CodeValueReason"].ToString()==listReasons[j].CodeValue && tableNotPerfs.Rows[i]["CodeSystemReason"].ToString()==listReasons[j].CodeSystem) { dictReasonNumEhrCode.Add(PIn.Long(tableNotPerfs.Rows[i]["EhrNotPerformedNum"].ToString()),listReasons[j]); break; } } } for(int i=0;i<tableNotPerfs.Rows.Count;i++) { EhrCqmNotPerf ehrNotPerfCur=new EhrCqmNotPerf(); ehrNotPerfCur.EhrCqmNotPerfNum=PIn.Long(tableNotPerfs.Rows[i]["EhrNotPerformedNum"].ToString()); ehrNotPerfCur.PatNum=PIn.Long(tableNotPerfs.Rows[i]["PatNum"].ToString()); ehrNotPerfCur.CodeValue=tableNotPerfs.Rows[i]["CodeValue"].ToString(); ehrNotPerfCur.CodeSystemName=tableNotPerfs.Rows[i]["CodeSystem"].ToString(); ehrNotPerfCur.CodeValueReason=tableNotPerfs.Rows[i]["CodeValueReason"].ToString(); ehrNotPerfCur.CodeSystemNameReason=tableNotPerfs.Rows[i]["CodeSystemReason"].ToString(); ehrNotPerfCur.DateEntry=PIn.Date(tableNotPerfs.Rows[i]["DateEntry"].ToString()); EhrCode itemEhrCode=dictItemNumEhrCode[ehrNotPerfCur.EhrCqmNotPerfNum]; ehrNotPerfCur.CodeSystemOID=itemEhrCode.CodeSystemOID; ehrNotPerfCur.ValueSetName=itemEhrCode.ValueSetName; ehrNotPerfCur.ValueSetOID=itemEhrCode.ValueSetOID; EhrCode reasonEhrCode=dictReasonNumEhrCode[ehrNotPerfCur.EhrCqmNotPerfNum]; ehrNotPerfCur.CodeSystemOIDReason=reasonEhrCode.CodeSystemOID; ehrNotPerfCur.ValueSetNameReason=reasonEhrCode.ValueSetName; ehrNotPerfCur.ValueSetOIDReason=reasonEhrCode.ValueSetOID; string descript=tableNotPerfs.Rows[i]["Description"].ToString(); if(descript=="") {//just in case not found in table, will default to description of EhrCode object descript=itemEhrCode.Description; } ehrNotPerfCur.Description=descript; string reasonDescript=tableNotPerfs.Rows[i]["DescriptionReason"].ToString(); if(reasonDescript=="") {//just in case not found in table, will default to description of EhrCode object reasonDescript=reasonEhrCode.Description; } ehrNotPerfCur.DescriptionReason=reasonDescript; if(retval.ContainsKey(ehrNotPerfCur.PatNum)) { retval[ehrNotPerfCur.PatNum].Add(ehrNotPerfCur); } else { retval.Add(ehrNotPerfCur.PatNum,new List<EhrCqmNotPerf>() { ehrNotPerfCur }); } } return retval; }