cqm-models 1.0.3 → 1.1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (188) hide show
  1. checksums.yaml +5 -5
  2. data/.eslintrc.json +10 -2
  3. data/.github/PULL_REQUEST_TEMPLATE.md +0 -4
  4. data/.rubocop.yml +3 -0
  5. data/.travis.yml +31 -1
  6. data/README.md +59 -2
  7. data/app/assets/javascripts/AdverseEvent.js +16 -7
  8. data/app/assets/javascripts/AllDataElements.js +6 -6
  9. data/app/assets/javascripts/AllergyIntolerance.js +16 -7
  10. data/app/assets/javascripts/AssessmentOrder.js +15 -6
  11. data/app/assets/javascripts/AssessmentPerformed.js +16 -7
  12. data/app/assets/javascripts/AssessmentRecommended.js +16 -7
  13. data/app/assets/javascripts/CareGoal.js +16 -7
  14. data/app/assets/javascripts/CommunicationPerformed.js +15 -6
  15. data/app/assets/javascripts/DeviceApplied.js +16 -7
  16. data/app/assets/javascripts/DeviceOrder.js +16 -7
  17. data/app/assets/javascripts/DeviceRecommended.js +16 -7
  18. data/app/assets/javascripts/Diagnosis.js +16 -7
  19. data/app/assets/javascripts/DiagnosticStudyOrder.js +16 -7
  20. data/app/assets/javascripts/DiagnosticStudyPerformed.js +16 -7
  21. data/app/assets/javascripts/DiagnosticStudyRecommended.js +16 -7
  22. data/app/assets/javascripts/EncounterOrder.js +16 -7
  23. data/app/assets/javascripts/EncounterPerformed.js +16 -7
  24. data/app/assets/javascripts/EncounterRecommended.js +16 -7
  25. data/app/assets/javascripts/FamilyHistory.js +16 -7
  26. data/app/assets/javascripts/Id.js +7 -3
  27. data/app/assets/javascripts/ImmunizationAdministered.js +16 -7
  28. data/app/assets/javascripts/ImmunizationOrder.js +16 -7
  29. data/app/assets/javascripts/IndividualResult.js +7 -2
  30. data/app/assets/javascripts/InterventionOrder.js +16 -7
  31. data/app/assets/javascripts/InterventionPerformed.js +16 -7
  32. data/app/assets/javascripts/InterventionRecommended.js +16 -7
  33. data/app/assets/javascripts/LaboratoryTestOrder.js +16 -7
  34. data/app/assets/javascripts/LaboratoryTestPerformed.js +16 -7
  35. data/app/assets/javascripts/LaboratoryTestRecommended.js +16 -7
  36. data/app/assets/javascripts/MedicationActive.js +16 -7
  37. data/app/assets/javascripts/MedicationAdministered.js +16 -7
  38. data/app/assets/javascripts/MedicationDischarge.js +16 -7
  39. data/app/assets/javascripts/MedicationDispensed.js +16 -7
  40. data/app/assets/javascripts/MedicationOrder.js +16 -7
  41. data/app/assets/javascripts/Participation.js +15 -6
  42. data/app/assets/javascripts/PatientCareExperience.js +16 -7
  43. data/app/assets/javascripts/PatientCharacteristic.js +16 -7
  44. data/app/assets/javascripts/PatientCharacteristicBirthdate.js +16 -7
  45. data/app/assets/javascripts/PatientCharacteristicClinicalTrialParticipant.js +16 -7
  46. data/app/assets/javascripts/PatientCharacteristicEthnicity.js +16 -7
  47. data/app/assets/javascripts/PatientCharacteristicExpired.js +16 -7
  48. data/app/assets/javascripts/PatientCharacteristicPayer.js +16 -7
  49. data/app/assets/javascripts/PatientCharacteristicRace.js +16 -7
  50. data/app/assets/javascripts/PatientCharacteristicSex.js +16 -7
  51. data/app/assets/javascripts/PhysicalExamOrder.js +16 -7
  52. data/app/assets/javascripts/PhysicalExamPerformed.js +16 -7
  53. data/app/assets/javascripts/PhysicalExamRecommended.js +16 -7
  54. data/app/assets/javascripts/ProcedureOrder.js +16 -7
  55. data/app/assets/javascripts/ProcedurePerformed.js +16 -7
  56. data/app/assets/javascripts/ProcedureRecommended.js +16 -7
  57. data/app/assets/javascripts/ProviderCareExperience.js +16 -7
  58. data/app/assets/javascripts/ProviderCharacteristic.js +16 -7
  59. data/app/assets/javascripts/QDMPatient.js +262 -0
  60. data/app/assets/javascripts/Result.js +7 -2
  61. data/app/assets/javascripts/ResultComponent.js +14 -6
  62. data/app/assets/javascripts/SubstanceAdministered.js +16 -7
  63. data/app/assets/javascripts/SubstanceOrder.js +16 -7
  64. data/app/assets/javascripts/SubstanceRecommended.js +16 -7
  65. data/app/assets/javascripts/Symptom.js +16 -7
  66. data/app/assets/javascripts/attributes/Component.js +30 -0
  67. data/app/assets/javascripts/attributes/FacilityLocation.js +30 -0
  68. data/app/assets/javascripts/basetypes/Any.js +1 -1
  69. data/app/assets/javascripts/basetypes/Code.js +1 -1
  70. data/app/assets/javascripts/basetypes/DataElement.js +2 -1
  71. data/app/assets/javascripts/basetypes/DateTime.js +5 -1
  72. data/app/assets/javascripts/basetypes/Interval.js +1 -1
  73. data/app/assets/javascripts/basetypes/Quantity.js +1 -1
  74. data/app/assets/javascripts/basetypes/Ratio.js +1 -1
  75. data/app/assets/javascripts/browser.js +3 -0
  76. data/app/assets/javascripts/cqm/AllCQMModels.js +24 -0
  77. data/app/assets/javascripts/cqm/CQLLibrary.js +32 -0
  78. data/app/assets/javascripts/cqm/CQLStatementDependency.js +28 -0
  79. data/app/assets/javascripts/cqm/Concept.js +17 -0
  80. data/app/assets/javascripts/cqm/Measure.js +108 -0
  81. data/app/assets/javascripts/{MeasurePackage.js → cqm/MeasurePackage.js} +9 -3
  82. data/app/assets/javascripts/cqm/Patient.js +38 -0
  83. data/app/assets/javascripts/cqm/PopulationSet.js +88 -0
  84. data/app/assets/javascripts/cqm/Provider.js +44 -0
  85. data/app/assets/javascripts/cqm/ValueSet.js +26 -0
  86. data/app/assets/javascripts/index.js +18 -8
  87. data/app/models/{qdm/tacoma → cqm}/concept.rb +4 -4
  88. data/app/models/cqm/cql_library.rb +22 -0
  89. data/app/models/cqm/cql_statement_dependency.rb +24 -0
  90. data/app/models/cqm/measure.rb +105 -0
  91. data/app/models/{qdm/tacoma → cqm}/measure_package.rb +2 -2
  92. data/app/models/cqm/patient.rb +20 -0
  93. data/app/models/cqm/population_set.rb +86 -0
  94. data/app/models/cqm/provider.rb +110 -0
  95. data/app/models/cqm/valueset.rb +15 -0
  96. data/app/models/hqmfOid_to_datatype_map.json +55 -0
  97. data/app/models/model_finder.rb +12 -0
  98. data/app/models/models.rb +18 -6
  99. data/app/models/qdm/adverse_event.rb +3 -2
  100. data/app/models/qdm/allergy_intolerance.rb +2 -1
  101. data/app/models/qdm/assessment_order.rb +1 -0
  102. data/app/models/qdm/assessment_performed.rb +2 -1
  103. data/app/models/qdm/assessment_recommended.rb +2 -1
  104. data/app/models/qdm/attributes/attribute.rb +63 -0
  105. data/app/models/qdm/{component.rb → attributes/component.rb} +1 -1
  106. data/app/models/qdm/{facility_location.rb → attributes/facility_location.rb} +1 -1
  107. data/app/models/qdm/basetypes/data_element.rb +21 -6
  108. data/app/models/qdm/basetypes/interval.rb +2 -1
  109. data/app/models/qdm/care_goal.rb +2 -1
  110. data/app/models/qdm/communication_performed.rb +1 -0
  111. data/app/models/qdm/device_applied.rb +2 -1
  112. data/app/models/qdm/device_order.rb +2 -1
  113. data/app/models/qdm/device_recommended.rb +2 -1
  114. data/app/models/qdm/diagnosis.rb +2 -1
  115. data/app/models/qdm/diagnostic_study_order.rb +2 -1
  116. data/app/models/qdm/diagnostic_study_performed.rb +3 -2
  117. data/app/models/qdm/diagnostic_study_recommended.rb +2 -1
  118. data/app/models/qdm/encounter_order.rb +3 -2
  119. data/app/models/qdm/encounter_performed.rb +2 -1
  120. data/app/models/qdm/encounter_recommended.rb +3 -2
  121. data/app/models/qdm/family_history.rb +2 -1
  122. data/app/models/qdm/immunization_administered.rb +2 -1
  123. data/app/models/qdm/immunization_order.rb +2 -1
  124. data/app/models/qdm/intervention_order.rb +2 -1
  125. data/app/models/qdm/intervention_performed.rb +2 -1
  126. data/app/models/qdm/intervention_recommended.rb +2 -1
  127. data/app/models/qdm/laboratory_test_order.rb +2 -1
  128. data/app/models/qdm/laboratory_test_performed.rb +2 -1
  129. data/app/models/qdm/laboratory_test_recommended.rb +2 -1
  130. data/app/models/qdm/medication_active.rb +2 -1
  131. data/app/models/qdm/medication_administered.rb +2 -1
  132. data/app/models/qdm/medication_discharge.rb +2 -1
  133. data/app/models/qdm/medication_dispensed.rb +4 -3
  134. data/app/models/qdm/medication_order.rb +3 -2
  135. data/app/models/qdm/participation.rb +1 -0
  136. data/app/models/qdm/patient.rb +7 -4
  137. data/app/models/qdm/patient_care_experience.rb +2 -1
  138. data/app/models/qdm/patient_characteristic.rb +2 -1
  139. data/app/models/qdm/patient_characteristic_birthdate.rb +2 -1
  140. data/app/models/qdm/patient_characteristic_clinical_trial_participant.rb +2 -1
  141. data/app/models/qdm/patient_characteristic_ethnicity.rb +2 -1
  142. data/app/models/qdm/patient_characteristic_expired.rb +2 -1
  143. data/app/models/qdm/patient_characteristic_payer.rb +2 -1
  144. data/app/models/qdm/patient_characteristic_race.rb +2 -1
  145. data/app/models/qdm/patient_characteristic_sex.rb +2 -1
  146. data/app/models/qdm/physical_exam_order.rb +2 -1
  147. data/app/models/qdm/physical_exam_performed.rb +2 -1
  148. data/app/models/qdm/physical_exam_recommended.rb +2 -1
  149. data/app/models/qdm/procedure_order.rb +2 -1
  150. data/app/models/qdm/procedure_performed.rb +2 -1
  151. data/app/models/qdm/procedure_recommended.rb +2 -1
  152. data/app/models/qdm/provider_care_experience.rb +2 -1
  153. data/app/models/qdm/provider_characteristic.rb +2 -1
  154. data/app/models/qdm/substance_administered.rb +2 -1
  155. data/app/models/qdm/substance_order.rb +2 -1
  156. data/app/models/qdm/substance_recommended.rb +2 -1
  157. data/app/models/qdm/symptom.rb +2 -1
  158. data/bin/build_cql_execution.sh +8 -0
  159. data/bin/validate_browser.sh +17 -0
  160. data/bin/validate_generator.sh +12 -0
  161. data/cqm-models.gemspec +5 -2
  162. data/data/oids_qdm_5.4.json +321 -0
  163. data/data/{oids.json → oids_unversioned.json} +0 -0
  164. data/dist/browser.js +102937 -0
  165. data/dist/index.js +36637 -36540
  166. data/lib/generate_models.rb +89 -93
  167. data/lib/generate_patients.rb +125 -0
  168. data/lib/generate_types.rb +64 -0
  169. data/lib/generators/custom_mongo/model.rb.tt +20 -0
  170. data/lib/generators/custom_mongo/model_generator.rb +34 -0
  171. data/notice.md +9 -0
  172. data/package.json +12 -6
  173. data/templates/id_template.js.erb +8 -3
  174. data/templates/index_template.js.erb +18 -8
  175. data/templates/models_template.rb.erb +20 -4
  176. data/templates/mongoose_template.js.erb +23 -15
  177. data/templates/patient_extension.rb.erb +7 -4
  178. data/templates/patient_template.js.erb +88 -87
  179. data/yarn.lock +552 -347
  180. metadata +86 -19
  181. data/app/assets/javascripts/Component.js +0 -26
  182. data/app/assets/javascripts/Concept.js +0 -14
  183. data/app/assets/javascripts/FacilityLocation.js +0 -26
  184. data/app/assets/javascripts/Measure.js +0 -78
  185. data/app/assets/javascripts/Patient.js +0 -262
  186. data/app/assets/javascripts/ValueSet.js +0 -30
  187. data/app/models/qdm/tacoma/measure.rb +0 -174
  188. data/app/models/qdm/tacoma/valueset.rb +0 -68
@@ -0,0 +1,44 @@
1
+ const mongoose = require('mongoose/browser');
2
+ const Id = require('../Id');
3
+
4
+ const [Schema, String, Boolean] = [
5
+ mongoose.Schema,
6
+ mongoose.Schema.Types.String,
7
+ mongoose.Schema.Types.Boolean,
8
+ ];
9
+
10
+ const AddressSchema = new mongoose.Schema({
11
+ street: [String],
12
+ city: String,
13
+ state: String,
14
+ zip: String,
15
+ country: String,
16
+ use: String,
17
+ });
18
+
19
+ const TelecomSchema = new mongoose.Schema({
20
+ use: String,
21
+ value: String,
22
+ preferred: Boolean,
23
+ });
24
+
25
+ const ProviderSchema = new Schema({
26
+ _type: { type: String, default: 'Provider' },
27
+
28
+ givenNames: [String],
29
+ familyName: String,
30
+ specialty: String,
31
+ title: String,
32
+ addresses: [AddressSchema],
33
+ telecoms: [TelecomSchema],
34
+ ids: [Id.IdSchema],
35
+
36
+ }, { id: false });
37
+
38
+ module.exports.ProviderSchema = ProviderSchema;
39
+ class Provider extends mongoose.Document {
40
+ constructor(object) {
41
+ super(object, ProviderSchema);
42
+ }
43
+ }
44
+ module.exports.Provider = Provider;
@@ -0,0 +1,26 @@
1
+ const mongoose = require('mongoose/browser');
2
+ const Concept = require('./Concept.js');
3
+
4
+ const [String] = [mongoose.Schema.Types.String];
5
+
6
+ const ValueSetSchema = new mongoose.Schema(
7
+ {
8
+ oid: String,
9
+ display_name: String,
10
+ version: String,
11
+
12
+ concepts: [Concept.ConceptSchema],
13
+ },
14
+ // Options
15
+ {
16
+ timestamps: { createdAt: 'created_at', updatedAt: 'updated_at' },
17
+ }
18
+ );
19
+
20
+ module.exports.ValueSetSchema = ValueSetSchema;
21
+ class ValueSet extends mongoose.Document {
22
+ constructor(object) {
23
+ super(object, ValueSetSchema);
24
+ }
25
+ }
26
+ module.exports.ValueSet = ValueSet;
@@ -2,13 +2,23 @@ module.exports = require('./AllDataElements.js');
2
2
  module.exports.CQL = require('cql-execution');
3
3
  module.exports.Result = require('./Result.js').Result;
4
4
  module.exports.ResultSchema = require('./Result.js').ResultSchema;
5
- module.exports.Measure = require('./Measure.js').Measure;
6
- module.exports.MeasureSchema = require('./Measure.js').MeasureSchema;
7
- module.exports.MeasurePackage = require('./MeasurePackage.js').MeasurePackage;
8
- module.exports.MeasurePackageSchema = require('./MeasurePackage.js').MeasurePackageSchema;
9
- module.exports.ValueSet = require('./ValueSet.js').ValueSet;
10
- module.exports.ValueSetSchema = require('./ValueSet.js').ValueSetSchema;
11
- module.exports.Concept = require('./Concept.js').Concept;
12
- module.exports.ConceptSchema = require('./Concept.js').ConceptSchema;
5
+ module.exports.Measure = require('./cqm/Measure.js').Measure;
6
+ module.exports.MeasureSchema = require('./cqm/Measure.js').MeasureSchema;
7
+ module.exports.MeasurePackage = require('./cqm/MeasurePackage.js').MeasurePackage;
8
+ module.exports.MeasurePackageSchema = require('./cqm/MeasurePackage.js').MeasurePackageSchema;
9
+ module.exports.Patient = require('./cqm/Patient.js').Patient;
10
+ module.exports.PatientSchema = require('./cqm/Patient.js').PatientSchema;
11
+ module.exports.Provider = require('./cqm/Provider.js').Provider;
12
+ module.exports.ProviderSchema = require('./cqm/Provider.js').ProviderSchema;
13
+ module.exports.StatementDependency = require('./cqm/CQLStatementDependency.js').StatementDependency;
14
+ module.exports.StatementDependencySchema = require('./cqm/CQLStatementDependency.js').StatementDependencySchema;
15
+ module.exports.PopulationSet = require('./cqm/PopulationSet.js').PopulationSet;
16
+ module.exports.PopulationSetSchema = require('./cqm/PopulationSet.js').PopulationSetSchema;
17
+ module.exports.CQLLibrary = require('./cqm/CQLLibrary.js').CQLLibrary;
18
+ module.exports.CQLLibrarySchema = require('./cqm/CQLLibrary.js').CQLLibrarySchema;
19
+ module.exports.ValueSet = require('./cqm/ValueSet.js').ValueSet;
20
+ module.exports.ValueSetSchema = require('./cqm/ValueSet.js').ValueSetSchema;
21
+ module.exports.Concept = require('./cqm/Concept.js').Concept;
22
+ module.exports.ConceptSchema = require('./cqm/Concept.js').ConceptSchema;
13
23
  module.exports.IndividualResult = require('./IndividualResult').IndividualResult;
14
24
  module.exports.IndividualResultSchema = require('./IndividualResult').IndividualResultSchema;
@@ -1,14 +1,14 @@
1
- module QDM
1
+ module CQM
2
2
  # Concept is an individual code within a ValueSet, including all of the associated metadata
3
3
  # such as code system, version, and display name
4
4
  class Concept
5
5
  include Mongoid::Document
6
6
  field :code, type: String
7
+ field :code_system_oid, type: String
7
8
  field :code_system_name, type: String
8
9
  field :code_system_version, type: String
9
10
  field :display_name, type: String
10
- field :code_system, type: String
11
- scope :by_code_system_name, ->(cs) { where(code_system_name: cs) }
12
- scope :by_code_system, ->(cs) { where(code_system: cs) }
11
+
12
+ embedded_in :value_set
13
13
  end
14
14
  end
@@ -0,0 +1,22 @@
1
+ module CQM
2
+ # CQLLibrary encapsulates what used to be a Hash in Ruby, by explicitly specifying
3
+ # a library ID, and the CQL string for that library
4
+ class CQLLibrary
5
+ include Mongoid::Document
6
+ embedded_in :measure
7
+
8
+ field :library_name, type: String
9
+ field :library_version, type: String
10
+ field :cql, type: String
11
+ field :elm, type: Hash
12
+ field :elm_annotations, type: Hash
13
+ field :is_main_library, type: Boolean, default: false
14
+
15
+ # Currently this is only relevant for cql libraries that are included in composite measures,
16
+ # true indicates the library files are in the "top level" folder of the measure package, rather
17
+ # than coming from a component measure folder.
18
+ field :is_top_level, type: Boolean, default: true
19
+
20
+ embeds_many :statement_dependencies, class_name: 'CQM::StatementDependency'
21
+ end
22
+ end
@@ -0,0 +1,24 @@
1
+ module CQM
2
+ # Statement is a given CQL Statement within a CQL Library, which has many dependencies
3
+ class StatementDependency
4
+ include Mongoid::Document
5
+ embedded_in :cql_library
6
+
7
+ field :statement_name, type: String
8
+ embeds_many :statement_references
9
+ end
10
+
11
+ # StatementReference notes a CQL Library/Statement which a given Statement (in which it is embedded) relies upon. This
12
+ # is also used for populations/stratifications/SDEs to reference their defining statement.
13
+ class StatementReference
14
+ include Mongoid::Document
15
+ embedded_in :statement_dependency
16
+ embedded_in :population_set
17
+ embedded_in :population_map
18
+ embedded_in :stratification
19
+
20
+ field :library_name, type: String
21
+ field :statement_name, type: String
22
+ field :hqmf_id, type: String
23
+ end
24
+ end
@@ -0,0 +1,105 @@
1
+ module CQM
2
+ # Measure contains the information necessary to represent the CQL version of a Clinical Quality Measure,
3
+ # As needed by the Bonnie & Cypress applications
4
+ class Measure
5
+ include Mongoid::Document
6
+ include Mongoid::Timestamps
7
+
8
+ # These are the Mongoid equivalent of the Mongoose "enum" attribute for the respective fields.
9
+ # They throw an error if you try to assign a value that's not in the array.
10
+ validates_inclusion_of :measure_scoring, in: %w[PROPORTION RATIO CONTINUOUS_VARIABLE COHORT]
11
+ validates_inclusion_of :calculation_method, in: %w[PATIENT EPISODE_OF_CARE]
12
+
13
+ IPP = 'IPP'.freeze
14
+ DENOM = 'DENOM'.freeze
15
+ NUMER = 'NUMER'.freeze
16
+ NUMEX = 'NUMEX'.freeze
17
+ DENEXCEP = 'DENEXCEP'.freeze
18
+ DENEX = 'DENEX'.freeze
19
+ MSRPOPL = 'MSRPOPL'.freeze
20
+ OBSERV = 'OBSERV'.freeze
21
+ MSRPOPLEX = 'MSRPOPLEX'.freeze
22
+
23
+ STRAT = 'STRAT'.freeze
24
+
25
+ ALL_POPULATION_CODES = [STRAT, IPP, DENOM, DENEX, NUMER, NUMEX, DENEXCEP, MSRPOPL, OBSERV, MSRPOPLEX].freeze
26
+
27
+ CQL_SKIP_STATEMENTS = ['SDE Ethnicity', 'SDE Payer', 'SDE Race', 'SDE Sex'].freeze
28
+
29
+ # A version-specific UUID for the measure
30
+ field :hqmf_id, type: String
31
+ # A version-neutral UUID for the measure
32
+ field :hqmf_set_id, type: String
33
+ # A Semantic Version-compliant string (e.g. "2.3.4") for the measure
34
+ field :hqmf_version_number, type: String
35
+ # A CMS-style string (e.g. "CMS2v4") for the measure
36
+ field :cms_id, type: String
37
+ field :title, type: String, default: ''
38
+ field :description, type: String, default: ''
39
+
40
+ # Composite/component measure fields
41
+ field :composite, type: Boolean, default: false
42
+ field :component, type: Boolean, default: false
43
+ field :component_hqmf_set_ids, type: Array, default: []
44
+ field :composite_hqmf_set_id, type: String
45
+
46
+ # Measure type variables
47
+ # Note: these are constrained to an enumeration of value options above, via validates_inclusion_of
48
+ field :measure_scoring, type: String, default: 'PROPORTION'
49
+ field :calculation_method, type: String, default: 'PATIENT'
50
+ field :calculate_sdes, type: Boolean
51
+
52
+ # ELM/CQL Measure-logic related data
53
+ # Field name changed from 'cql' to 'cql_libraries' because the semantics of
54
+ # embeds_many :cqls sounded weird
55
+ embeds_many :cql_libraries, class_name: 'CQM::CQLLibrary'
56
+ field :main_cql_library, type: String
57
+
58
+ # HQMF/Tacoma-specific Measure-logic related data
59
+ field :population_criteria, type: Hash
60
+ field :measure_period, type: Hash
61
+ field :measure_attributes, type: Array
62
+ embeds_many :source_data_criteria, class_name: 'QDM::DataElement'
63
+
64
+ embeds_many :population_sets
65
+
66
+ # Relations to other model classes
67
+ # Note: bundle is removed, Cypress may create their own bundle object and inject the relationship
68
+ has_one :package, class_name: 'CQM::MeasurePackage', inverse_of: :measure, dependent: :destroy # Bonnie-specific
69
+
70
+ # Store this references as an Array on the Measure object,
71
+ # but don't care about the inverse relationship (e.g. we never really care
72
+ # about getting all the measures from a ValueSet object,
73
+ # hence the 'inverse_of: nil')
74
+ has_and_belongs_to_many :value_sets, inverse_of: nil
75
+
76
+ def all_stratifications
77
+ population_sets.flat_map(&:stratifications)
78
+ end
79
+
80
+ # Returns the hqmf-parser's ruby implementation of an HQMF document.
81
+ # Rebuild from population_criteria, data_criteria, and measure_period JSON
82
+ def as_hqmf_model
83
+ json = {
84
+ 'id' => hqmf_id,
85
+ 'title' => title,
86
+ 'description' => description,
87
+ 'population_criteria' => population_criteria,
88
+ 'data_criteria' => data_criteria,
89
+ 'source_data_criteria' => source_data_criteria,
90
+ 'measure_period' => measure_period,
91
+ 'attributes' => measure_attributes,
92
+ 'populations' => populations,
93
+ 'hqmf_id' => hqmf_id,
94
+ 'hqmf_set_id' => hqmf_set_id,
95
+ 'hqmf_version_number' => hqmf_version_number,
96
+ 'cms_id' => cms_id
97
+ }
98
+ HQMF::Document.from_json(json)
99
+ end
100
+
101
+ def all_data_criteria
102
+ as_hqmf_model.all_data_criteria
103
+ end
104
+ end
105
+ end
@@ -1,10 +1,10 @@
1
- module QDM
1
+ module CQM
2
2
  # MeasurePackage stores the uploaded file for a given Measure, so it can be recreated if need be
3
3
  class MeasurePackage
4
4
  include Mongoid::Document
5
5
  include Mongoid::Timestamps
6
6
 
7
7
  field :file, type: BSON::Binary
8
- belongs_to :measure, class_name: 'Measure', inverse_of: :package
8
+ belongs_to :measure, class_name: 'CQM::Measure', inverse_of: :package
9
9
  end
10
10
  end
@@ -0,0 +1,20 @@
1
+ module CQM
2
+ # Patient model that holds non-QDM data for the patient
3
+ class Patient
4
+ include Mongoid::Document
5
+ field :givenNames, type: Array
6
+ field :familyName, type: String
7
+ field :bundleId, type: String
8
+ field :expectedValues, type: Array
9
+ field :notes, type: String
10
+ field :measure_ids, type: Array
11
+
12
+ has_and_belongs_to_many :providers, class_name: 'CQM::Provider'
13
+ embeds_one :qdmPatient, class_name: 'QDM::Patient', autobuild: true
14
+
15
+ # Include '_type' in any JSON output. This is necessary for deserialization.
16
+ def to_json(options = nil)
17
+ serializable_hash(include: :_type, methods: :_type).to_json(options)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,86 @@
1
+ module CQM
2
+ # Representation of a population set (aka. PopulationCriteria)
3
+ class PopulationSet
4
+ include Mongoid::Document
5
+
6
+ embedded_in :measure
7
+
8
+ embeds_one :populations, class_name: 'CQM::PopulationMap'
9
+ embeds_many :stratifications
10
+ embeds_many :supplemental_data_elements, class_name: 'CQM::StatementReference'
11
+ embeds_many :observations
12
+
13
+ field :title, type: String
14
+ field :population_set_id, type: String
15
+ end
16
+
17
+ # Stratification info
18
+ class Stratification
19
+ include Mongoid::Document
20
+
21
+ embedded_in :population_set
22
+
23
+ field :title, type: String
24
+ field :stratification_id, type: String
25
+ field :hqmf_id, type: String
26
+ embeds_one :statement, class_name: 'CQM::StatementReference'
27
+ end
28
+
29
+ # Observation info
30
+ class Observation
31
+ include Mongoid::Document
32
+
33
+ embedded_in :population_set
34
+
35
+ embeds_one :observation_function, class_name: 'CQM::StatementReference'
36
+ embeds_one :observation_parameter, class_name: 'CQM::StatementReference'
37
+ field :aggregation_type, type: String
38
+ field :hqmf_id, type: String
39
+ end
40
+
41
+ # Base class for the population maps
42
+ class PopulationMap
43
+ include Mongoid::Document
44
+
45
+ embedded_in :population_set
46
+ end
47
+
48
+ # The population map for a porportion population set
49
+ class ProportionPopulationMap < PopulationMap
50
+ include Mongoid::Document
51
+
52
+ embeds_one :IPP, class_name: 'CQM::StatementReference'
53
+ embeds_one :DENOM, class_name: 'CQM::StatementReference'
54
+ embeds_one :NUMER, class_name: 'CQM::StatementReference'
55
+ embeds_one :NUMEX, class_name: 'CQM::StatementReference'
56
+ embeds_one :DENEX, class_name: 'CQM::StatementReference'
57
+ embeds_one :DENEXCEP, class_name: 'CQM::StatementReference'
58
+ end
59
+
60
+ # The population map for a ratio population set
61
+ class RatioPopulationMap < PopulationMap
62
+ include Mongoid::Document
63
+
64
+ embeds_one :IPP, class_name: 'CQM::StatementReference'
65
+ embeds_one :DENOM, class_name: 'CQM::StatementReference'
66
+ embeds_one :NUMER, class_name: 'CQM::StatementReference'
67
+ embeds_one :NUMEX, class_name: 'CQM::StatementReference'
68
+ embeds_one :DENEX, class_name: 'CQM::StatementReference'
69
+ end
70
+
71
+ # The population map for a continuous variable population set
72
+ class ContinuousVariablePopulationMap < PopulationMap
73
+ include Mongoid::Document
74
+
75
+ embeds_one :IPP, class_name: 'CQM::StatementReference'
76
+ embeds_one :MSRPOPL, class_name: 'CQM::StatementReference'
77
+ embeds_one :MSRPOPLEX, class_name: 'CQM::StatementReference'
78
+ end
79
+
80
+ # The population map for a cohort population set
81
+ class CohortPopulationMap < PopulationMap
82
+ include Mongoid::Document
83
+
84
+ embeds_one :IPP, class_name: 'CQM::StatementReference'
85
+ end
86
+ end
@@ -0,0 +1,110 @@
1
+ module CQM
2
+ # Provider model that holds non-QDM data for the provider. As well as indentifiers referenced in QDM
3
+ class Provider
4
+ include Mongoid::Document
5
+
6
+ NPI_OID = '2.16.840.1.113883.4.6'.freeze
7
+ TAX_ID_OID = '2.16.840.1.113883.4.2'.freeze
8
+ CCN_OID = '2.16.840.1.113883.4.336'.freeze
9
+
10
+ field :givenNames, type: Array
11
+ field :familyName, type: String
12
+ field :specialty, type: String
13
+ field :title, type: String
14
+
15
+ validates_uniqueness_of :npi, allow_blank: true
16
+
17
+ embeds_many :addresses
18
+ embeds_many :telecoms
19
+ embeds_many :ids, class_name: 'QDM::Id'
20
+
21
+ def npi=(an_npi)
22
+ cda_id_npi = ids.where(namingSystem: NPI_OID).first
23
+ if cda_id_npi
24
+ cda_id_npi.value = an_npi
25
+ cda_id_npi.save!
26
+ else
27
+ ids << QDM::Id.new(namingSystem: NPI_OID, value: an_npi)
28
+ end
29
+ end
30
+
31
+ def npi
32
+ cda_id_npi = ids.where(namingSystem: NPI_OID).first
33
+ cda_id_npi ? cda_id_npi.value : nil
34
+ end
35
+
36
+ def tin=(a_tin)
37
+ ids << QDM::Id.new(namingSystem: TAX_ID_OID, value: a_tin)
38
+ end
39
+
40
+ def tin
41
+ cda_id_tin = ids.where(namingSystem: TAX_ID_OID).first
42
+ cda_id_tin ? cda_id_tin.value : nil
43
+ end
44
+
45
+ def ccn=(a_ccn)
46
+ cda_id_ccn = ids.where(namingSystem: CCN_OID).first
47
+ if cda_id_ccn
48
+ cda_id_ccn.value = a_ccn
49
+ cda_id_ccn.save!
50
+ else
51
+ ids << QDM::Id.new(namingSystem: CCN_OID, value: a_ccn)
52
+ end
53
+ end
54
+
55
+ def ccn
56
+ cda_id_ccn = ids.where(namingSystem: CCN_OID).first
57
+ cda_id_ccn ? cda_id_ccn.value : nil
58
+ end
59
+
60
+ # validate the NPI, should be 10 or 15 digits total with the final digit being a
61
+ # checksum using the Luhn algorithm with additional special handling as described in
62
+ # https://www.cms.gov/NationalProvIdentStand/Downloads/NPIcheckdigit.pdf
63
+ def self.valid_npi?(npi)
64
+ return false unless npi
65
+ return false if npi.length != 10 && npi.length != 15
66
+ return false if npi.gsub(/\d/, '').length.positive? # npi must be all digits
67
+ return false if npi.length == 15 && (npi =~ /^80840/).nil? # 15 digit npi must start with 80840
68
+
69
+ # checksum is always calculated as if 80840 prefix is present
70
+ npi = '80840' + npi if npi.length == 10
71
+
72
+ luhn_checksum(npi[0, 14]) == npi[14]
73
+ end
74
+
75
+ def self.luhn_checksum(num)
76
+ double = { '0' => 0, '1' => 2, '2' => 4, '3' => 6, '4' => 8, '5' => 1, '6' => 3, '7' => 5, '8' => 7, '9' => 9 }
77
+ sum = 0
78
+ num.reverse!
79
+ num.split('').each_with_index do |char, i|
80
+ sum += if (i % 2).zero?
81
+ double[char]
82
+ else
83
+ char.to_i
84
+ end
85
+ end
86
+ sum = (9 * sum) % 10
87
+
88
+ sum.to_s
89
+ end
90
+ end
91
+
92
+ # Model for a standard postal address
93
+ class Address
94
+ include Mongoid::Document
95
+ field :street, type: Array, default: []
96
+ field :city, type: String
97
+ field :state, type: String
98
+ field :zip, type: String
99
+ field :country, type: String
100
+ field :use, type: String
101
+ end
102
+
103
+ # Model for a standard telephone number
104
+ class Telecom
105
+ include Mongoid::Document
106
+ field :use, type: String
107
+ field :value, type: String
108
+ field :preferred, type: Boolean
109
+ end
110
+ end