cqm-models 0.7.7 → 0.8.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9ed7947882634a78fa7ab8d84693d49b73f00553
4
- data.tar.gz: bfaac070cd031d3cb5e1ad431c7718ceec80ae24
3
+ metadata.gz: adce07d4828edeec55d0a07a98a55f7cd4ca0e6f
4
+ data.tar.gz: fd48e3958c11556440fa984040a426437fb65823
5
5
  SHA512:
6
- metadata.gz: 37c4e67ed09e024af36f767242370acd207d21e3432e04720f9de5ec870d1df27f5ccfd5c366034e1f1cc29d974d77b598f297c4050c90e8e0cce1555f66d538
7
- data.tar.gz: 8e635c6f718e8a42def5bfb34983cf4184a4f3ebca748ca25d1f82027aa46280739101d3654f1f7df2c4f1111e4bce28ffdb34c46f9af0a1092ba251a7d77e2a
6
+ metadata.gz: 34e5edecfca3aebe628265178824100649c0abadce2b6c730acf7ed368f1d6c958606d12e31fa9a1e9a8c1bc0a80bd3a46daa68ab52123604eafea31e8f81010
7
+ data.tar.gz: 219d45b6e063aaa360eb8800b6f84fe496fb70625589f5417f0203dc1ad98a64d9253fe1d7592a41502b646ef08e6040dc3de410fa961aba42ba60cdd62f641b
data/.rubocop.yml CHANGED
@@ -37,6 +37,9 @@ Naming/UncommunicativeMethodParamName:
37
37
  Naming/VariableName:
38
38
  Enabled: false
39
39
 
40
+ Style/DateTime:
41
+ Enabled: false
42
+
40
43
  # Only disabled until the cyclomatic complexity method in measure.rb (ported from bonnie-bundler) is rewritten
41
44
  Lint/NestedMethodDefinition:
42
45
  Enabled: false
data/README.md CHANGED
@@ -1,5 +1,6 @@
1
1
  [![Build Status](https://travis-ci.org/projecttacoma/cqm-models.svg?branch=master)](https://travis-ci.org/projecttacoma/cqm-models)
2
2
  [![Gem Version](https://badge.fury.io/rb/cqm-models.svg)](https://badge.fury.io/rb/cqm-models)
3
+ ![NPM](https://img.shields.io/npm/v/cqm-models.svg)
3
4
 
4
5
  # cqm-models
5
6
 
@@ -0,0 +1,54 @@
1
+ const mongoose = require('mongoose');
2
+
3
+ const [Number, String, Mixed, ObjectId] = [
4
+ mongoose.Schema.Types.Number,
5
+ mongoose.Schema.Types.String,
6
+ mongoose.Schema.Types.Mixed,
7
+ mongoose.Schema.Types.ObjectId,
8
+ ];
9
+
10
+ const IndividualResultSchema = mongoose.Schema(
11
+ {
12
+ // Population Attributes
13
+ STRAT: Number,
14
+ IPP: Number,
15
+ DENOM: Number,
16
+ NUMER: Number,
17
+ NUMEX: Number,
18
+ DENEX: Number,
19
+ DENEXCEP: Number,
20
+ MSRPOPL: Number,
21
+ OBSERV: Number,
22
+ MSRPOPLEX: Number,
23
+
24
+ // Result Attributes
25
+ clause_results: Mixed,
26
+ episode_results: Mixed,
27
+ statement_results: Mixed,
28
+
29
+ // This field is for application specific information only. If both Bonnie and
30
+ // Cypress use a common field, it should be made a field on this model,
31
+ // and not put into extendedData.
32
+ extendedData: {},
33
+
34
+ // Calculation State attributes
35
+ state: {
36
+ type: String,
37
+ enum: ['queued', 'running', 'complete', 'cancelled', 'failed'],
38
+ default: 'queued',
39
+ },
40
+
41
+ // Relations to other model classes
42
+ // 'alias' field makes it so you can call obj.measure, and get the object referenced by measure_id
43
+ measure_id: { type: ObjectId, ref: 'Measure', alias: 'measure' },
44
+ patient_id: { type: ObjectId, ref: 'Patient', alias: 'patient' },
45
+
46
+ },
47
+ // Options
48
+ {
49
+ timestamps: { createdAt: 'created_at', updatedAt: 'updated_at' }, // These are the Mongoid conventions for timestamps
50
+ }
51
+ );
52
+
53
+ module.exports.IndividualResultSchema = IndividualResultSchema;
54
+ module.exports.IndividualResult = mongoose.model('individual_result', IndividualResultSchema);
@@ -3,7 +3,7 @@ const Code = require('./basetypes/Code');
3
3
  const Interval = require('./basetypes/Interval');
4
4
  const Quantity = require('./basetypes/Quantity');
5
5
 
6
- const [mNumber, mString, mBoolean, Mixed, ObjectId, mDate] = [
6
+ const [Number, String, Boolean, Mixed, ObjectId, Date] = [
7
7
  mongoose.Schema.Types.Number,
8
8
  mongoose.Schema.Types.String,
9
9
  mongoose.Schema.Types.Boolean,
@@ -15,32 +15,32 @@ const [mNumber, mString, mBoolean, Mixed, ObjectId, mDate] = [
15
15
  const MeasureSchema = mongoose.Schema(
16
16
  {
17
17
  // ID/other measure information
18
- id: mString,
19
- measure_id: mString,
20
- hqmf_id: mString,
21
- hqmf_set_id: mString,
22
- hqmf_version_number: mNumber,
23
- cms_id: mString,
24
- title: mString,
25
- description: mString,
26
- type: mString,
27
- category: { type: mString, default: 'Uncategorized' },
18
+ id: String,
19
+ measure_id: String,
20
+ hqmf_id: String,
21
+ hqmf_set_id: String,
22
+ hqmf_version_number: Number,
23
+ cms_id: String,
24
+ title: String,
25
+ description: String,
26
+ type: String,
27
+ category: { type: String, default: 'Uncategorized' },
28
28
 
29
29
  // Measure type variables
30
- episode_of_care: mBoolean,
31
- continuous_constiable: mBoolean,
30
+ episode_of_care: Boolean,
31
+ continuous_constiable: Boolean,
32
32
  episode_ids: [],
33
33
 
34
34
  // Publishing data (used by Bonnie)
35
- published: mBoolean,
36
- publish_date: mDate,
37
- version: mNumber,
35
+ published: Boolean,
36
+ publish_date: Date,
37
+ version: Number,
38
38
 
39
39
  // ELM/CQL Measure-logic related data
40
40
  elm_annotations: Mixed,
41
- cql: [mString],
41
+ cql: [String],
42
42
  elm: [Mixed],
43
- main_cql_library: mString,
43
+ main_cql_library: String,
44
44
  cql_statement_dependencies: Mixed,
45
45
 
46
46
  // HQMF/Tacoma-specific Measure-logic related data
@@ -97,7 +97,8 @@ PatientSchema.methods.findRecords = function findRecords(profile) {
97
97
  let profileStripped;
98
98
  if (profile === 'Patient') {
99
99
  // Requested generic patient info
100
- return { birthDatetime: this.birthDatetime };
100
+ const info = { birthDatetime: this.birthDatetime };
101
+ return [info];
101
102
  } else if (/PatientCharacteristic/.test(profile)) {
102
103
  // Requested a patient characteristic
103
104
  profileStripped = profile.replace(/ *\{[^)]*\} */g, '');
@@ -1,7 +1,7 @@
1
1
  const mongoose = require('mongoose');
2
2
  const Concept = require('./Concept.js');
3
3
 
4
- const [mString, Mixed, ObjectId] = [
4
+ const [String, Mixed, ObjectId] = [
5
5
  mongoose.Schema.Types.String,
6
6
  mongoose.Schema.Types.Mixed,
7
7
  mongoose.Schema.Types.ObjectId,
@@ -9,9 +9,9 @@ const [mString, Mixed, ObjectId] = [
9
9
 
10
10
  const ValueSetSchema = mongoose.Schema(
11
11
  {
12
- oid: mString,
13
- display_name: mString,
14
- version: mString,
12
+ oid: String,
13
+ display_name: String,
14
+ version: String,
15
15
  categories: Mixed,
16
16
 
17
17
  concepts: [Concept.ConceptSchema],
@@ -10,3 +10,5 @@ module.exports.ValueSet = require('./ValueSet.js').ValueSet;
10
10
  module.exports.ValueSetSchema = require('./ValueSet.js').ValueSetSchema;
11
11
  module.exports.Concept = require('./Concept.js').Concept;
12
12
  module.exports.ConceptSchema = require('./Concept.js').ConceptSchema;
13
+ module.exports.IndividualResult = require('./IndividualResult').IndividualResult;
14
+ module.exports.IndividualResultSchema = require('./IndividualResult').IndividualResultSchema;
data/app/models/models.rb CHANGED
@@ -14,6 +14,7 @@ require_relative 'qdm/tacoma/measure'
14
14
  require_relative 'qdm/tacoma/measure_package'
15
15
  require_relative 'qdm/tacoma/valueset'
16
16
  require_relative 'qdm/tacoma/concept'
17
+ require_relative 'qdm/tacoma/individual_result'
17
18
 
18
19
  # Generated models
19
20
  require_relative 'qdm/patient'
@@ -47,6 +47,23 @@ module QDM
47
47
  serializable_hash(methods: :_type).to_json(options)
48
48
  end
49
49
 
50
+ # Shift all fields that deal with dates by the given value.
51
+ # Given value should be in seconds. Positive values shift forward, negative
52
+ # values shift backwards.
53
+ def shift_dates(seconds)
54
+ # Iterate over fields
55
+ fields.keys.each do |field|
56
+ # Check if field is a DateTime
57
+ if send(field).is_a? DateTime
58
+ send(field + '=', (send(field).to_time + seconds.seconds).to_datetime)
59
+ end
60
+ # Check if field is an Interval
61
+ if (send(field).is_a? Interval) || (send(field).is_a? DataElement)
62
+ send(field + '=', send(field).shift_dates(seconds))
63
+ end
64
+ end
65
+ end
66
+
50
67
  class << self
51
68
  # Get the object as it was stored in the database, and instantiate
52
69
  # this custom class from it.
@@ -16,6 +16,21 @@ module QDM
16
16
  { low: @low, high: @high, lowClosed: @lowClosed, highClosed: @highClosed, _type: 'QDM::Interval' }
17
17
  end
18
18
 
19
+ # Shift dates by the given value.
20
+ # Given value should be in seconds. Positive values shift forward, negative
21
+ # values shift backwards.
22
+ #
23
+ # NOTE: This will only shift if @high and @low are DateTimes.
24
+ def shift_dates(seconds)
25
+ if (@low.is_a? DateTime) || (@low.is_a? Time)
26
+ @low = (@low.utc.to_time + seconds.seconds).to_datetime.new_offset(0)
27
+ end
28
+ if (@high.is_a? DateTime) || (@high.is_a? Time)
29
+ @high = (@high.utc.to_time + seconds.seconds).to_datetime.new_offset(0)
30
+ end
31
+ self
32
+ end
33
+
19
34
  class << self
20
35
  # Get the object as it was stored in the database, and instantiate
21
36
  # this custom class from it.
@@ -25,6 +40,7 @@ module QDM
25
40
  def demongoize(object)
26
41
  return nil unless object
27
42
  object = object.symbolize_keys
43
+ fix_datetime(object)
28
44
  QDM::Interval.new(object[:low], object[:high], object[:lowClosed], object[:highClosed]) if object.is_a?(Hash)
29
45
  end
30
46
 
@@ -36,11 +52,18 @@ module QDM
36
52
  when QDM::Interval then object.mongoize
37
53
  when Hash
38
54
  object = object.symbolize_keys
55
+ fix_datetime(object)
39
56
  QDM::Interval.new(object[:low], object[:high], object[:lowClosed], object[:highClosed]).mongoize
40
57
  else object
41
58
  end
42
59
  end
43
60
 
61
+ def fix_datetime(object)
62
+ # Cast to DateTime if it is a string representing a DateTime
63
+ object[:low] = DateTime.parse(object[:low]) if (object[:low].is_a? String) && DateTime.parse(object[:low])
64
+ object[:high] = DateTime.parse(object[:high]) if (object[:high].is_a? String) && DateTime.parse(object[:high])
65
+ end
66
+
44
67
  # Converts the object that was supplied to a criteria and converts it
45
68
  # into a database friendly form.
46
69
  def evolve(object)
@@ -29,6 +29,17 @@ module QDM
29
29
  dataElements.where(qrdaOid: qrda_oid) || []
30
30
  end
31
31
 
32
+ # Shift all data element fields that deal with dates by the given value.
33
+ # Given value should be in seconds. Positive values shift forward, negative
34
+ # values shift backwards.
35
+ #
36
+ # Note: This will shift dates of the birthdate and
37
+ # dates on the data elements that exist on the patient.
38
+ def shift_dates(seconds)
39
+ self.birthDatetime = (birthDatetime.utc.to_time + seconds.seconds).to_datetime.new_offset(0)
40
+ dataElements.each { |element| element.shift_dates(seconds) }
41
+ end
42
+
32
43
  # Returns an array of elements that exist on this patient. Optionally
33
44
  # takes a category and/or, which returns all elements of that QDM
34
45
  # category. Example: patient.get_data_elements('encounters')
@@ -0,0 +1,39 @@
1
+ module QDM
2
+ # IndividualResult stores the patient-level (population/clause) results for a patient/measure combination
3
+ class IndividualResult
4
+ include Mongoid::Document
5
+ include Mongoid::Timestamps
6
+
7
+ # This is the Mongoid equivalent of the Mongoose "enum" attribute for :state. Throws an error if you try to assign a value that's not in this array.
8
+ validates_inclusion_of :state, in: %w[queued running complete cancelled failed]
9
+
10
+ # Population Attributes
11
+ field :STRAT, type: Integer
12
+ field :IPP, type: Integer
13
+ field :DENOM, type: Integer
14
+ field :NUMER, type: Integer
15
+ field :NUMEX, type: Integer
16
+ field :DENEX, type: Integer
17
+ field :DENEXCEP, type: Integer
18
+ field :MSRPOPL, type: Integer
19
+ field :OBSERV, type: Float
20
+ field :MSRPOPLEX, type: Integer
21
+
22
+ # Result Attributes
23
+ field :clause_results, type: Hash
24
+ field :episode_results, type: Hash
25
+ field :statement_results, type: Hash
26
+
27
+ # This field is for application specific information only. If both Bonnie and
28
+ # Cypress use a common field, it should be made a field on this model,
29
+ # and not put into extendedData.
30
+ field :extendedData, type: Hash
31
+
32
+ # Calculation state attributes
33
+ field :state, type: String, default: 'queued'
34
+
35
+ # Relations to other model classes
36
+ belongs_to :measure
37
+ belongs_to :patient
38
+ end
39
+ end
@@ -74,7 +74,7 @@ module QDM
74
74
  # Relations to other model classes
75
75
  belongs_to :user
76
76
  belongs_to :bundle, class_name: 'HealthDataStandards::CQM::Bundle'
77
- has_and_belongs_to_many :records, inverse_of: nil
77
+ has_and_belongs_to_many :patients, inverse_of: nil
78
78
  has_one :package, class_name: 'CqlMeasurePackage', inverse_of: :measure, dependent: :destroy
79
79
 
80
80
  scope :by_measure_id, ->(id) { where('measure_id' => id) }
data/cqm-models.gemspec CHANGED
@@ -4,7 +4,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
 
5
5
  Gem::Specification.new do |spec|
6
6
  spec.name = 'cqm-models'
7
- spec.version = '0.7.7'
7
+ spec.version = '0.8.0'
8
8
  spec.authors = ['aholmes@mitre.org', 'mokeefe@mitre.org', 'lades@mitre.org']
9
9
 
10
10
  spec.summary = 'Mongo models that correspond to the QDM specification.'