aqi 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +1 -0
- data/aqi.gemspec +19 -0
- data/lib/aqi/anesthesia_case.rb +34 -0
- data/lib/aqi/anesthesia_detail.rb +10 -0
- data/lib/aqi/anesthesia_record.rb +83 -0
- data/lib/aqi/anesthesia_records.rb +30 -0
- data/lib/aqi/anesthesia_staff.rb +42 -0
- data/lib/aqi/cpt.rb +26 -0
- data/lib/aqi/demographic.rb +26 -0
- data/lib/aqi/ic_event.rb +51 -0
- data/lib/aqi/intake_output_set.rb +13 -0
- data/lib/aqi/intubation_detail.rb +16 -0
- data/lib/aqi/outcome.rb +94 -0
- data/lib/aqi/outcome_events.rb +41 -0
- data/lib/aqi/pre_op.rb +109 -0
- data/lib/aqi/procedure.rb +70 -0
- data/lib/aqi/procedure_location.rb +34 -0
- data/lib/aqi/record_header.rb +18 -0
- data/lib/aqi/version.rb +3 -0
- data/lib/aqi.rb +45 -0
- metadata +70 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Robert Jackson
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# Aqi
|
2
|
+
|
3
|
+
TODO: Write a gem description
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'aqi'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install aqi
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
TODO: Write usage instructions here
|
22
|
+
|
23
|
+
## Contributing
|
24
|
+
|
25
|
+
1. Fork it
|
26
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
27
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
28
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
29
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/aqi.gemspec
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'aqi/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "aqi"
|
8
|
+
gem.version = AQI::VERSION
|
9
|
+
gem.authors = ["Robert Jackson"]
|
10
|
+
gem.email = ["robertj@promedicalinc.com"]
|
11
|
+
gem.description = %q{Generates output based on the AQI schema.}
|
12
|
+
gem.summary = %q{Generates output based on the AQI schema.}
|
13
|
+
gem.homepage = "https://github.com/promedical/aqi"
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ["lib"]
|
19
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module AQI
|
2
|
+
class AnesthesiaCase
|
3
|
+
attr_accessor :anesthesia_record_id, :anesthesia_coverage, :anesthesia_staff_set, :monitoring_set
|
4
|
+
|
5
|
+
def self.anesthesia_coverages
|
6
|
+
["MD-ALONE", "MD-DIRECTING", "CRNA-DIRECTED", "CRNA-ALONE",
|
7
|
+
"MD-SUPERVISING", "CRNA-DIRECTING", "MD-MD"]
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(options)
|
11
|
+
self.anesthesia_record_id = options[:anesthesia_record_id]
|
12
|
+
self.anesthesia_coverage = validate_anesthesia_coverage(options[:anesthesia_coverage])
|
13
|
+
self.anesthesia_staff_set = options[:anesthesia_staff_set] || []
|
14
|
+
self.monitoring_set = options[:monitoring_set] || []
|
15
|
+
end
|
16
|
+
|
17
|
+
def validate_anesthesia_coverage(value)
|
18
|
+
self.class.anesthesia_coverages.include?(value) ? value : nil
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_xml
|
22
|
+
builder = Builder::XmlMarkup.new
|
23
|
+
builder.AnesthesiaCase do |ac|
|
24
|
+
ac.AnesthesiaRecordID(anesthesia_record_id)
|
25
|
+
ac.AnesthesiaCoverage(anesthesia_coverage) if anesthesia_coverage
|
26
|
+
ac.AnesthesiaStaffSet do |ass|
|
27
|
+
anesthesia_staff_set.each do |as|
|
28
|
+
ass << as.to_xml
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module AQI
|
2
|
+
class AnesthesiaDetails
|
3
|
+
attr_accessor :intake_output_set_type, :intubation_details, :anesthesia_detils_set, :medications_total_set
|
4
|
+
|
5
|
+
def initialize(options)
|
6
|
+
self.intake_output_set_type = IntakeOutputSet.new(options.delete(:intake_output_set))
|
7
|
+
self.intubation_details = IntubationDetails.new(options.delete(:intubation_details))
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module AQI
|
2
|
+
class AnesthesiaRecord
|
3
|
+
attr_accessor :encounter
|
4
|
+
|
5
|
+
def initialize(encounter)
|
6
|
+
self.encounter = encounter
|
7
|
+
end
|
8
|
+
|
9
|
+
def to_xml
|
10
|
+
builder = Builder::XmlMarkup.new
|
11
|
+
builder.AnesthesiaRecord do |ar|
|
12
|
+
ar << demographic.to_xml
|
13
|
+
ar << procedure.to_xml
|
14
|
+
ar << anesthesia_case.to_xml
|
15
|
+
ar << pre_op.to_xml
|
16
|
+
ar << outcome_events.to_xml
|
17
|
+
#ar << post_op
|
18
|
+
#ar << timing_milestones
|
19
|
+
#ar << outcomes_events
|
20
|
+
#ar << anesthesia_details
|
21
|
+
#ar << ic_event_set
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def demographic
|
26
|
+
Demographic.new(date_of_birth: encounter.date_of_birth,
|
27
|
+
postal_code: encounter.postal_code,
|
28
|
+
state: encounter.state,
|
29
|
+
city: encounter.city,
|
30
|
+
gender: encounter.gender)
|
31
|
+
end
|
32
|
+
|
33
|
+
def outcome_events
|
34
|
+
outcomes = encounter.adverse_events.collect do |ae|
|
35
|
+
{time_date: encounter.date_of_service, name: ae}
|
36
|
+
end
|
37
|
+
|
38
|
+
OutcomeEvents.new(outcomes: outcomes)
|
39
|
+
end
|
40
|
+
|
41
|
+
def services
|
42
|
+
@services ||= encounter.services + encounter.pqrs_services
|
43
|
+
end
|
44
|
+
|
45
|
+
def start_time
|
46
|
+
services.map{|s| s.start_time}.min
|
47
|
+
end
|
48
|
+
|
49
|
+
def end_time
|
50
|
+
services.map{|s| s.end_time}.max
|
51
|
+
end
|
52
|
+
|
53
|
+
def cpt_set
|
54
|
+
services.collect{|s| CPT.new(value: s.procedure_code)}
|
55
|
+
end
|
56
|
+
|
57
|
+
def procedure
|
58
|
+
Procedure.new(procedure_id: encounter.encounter_id,
|
59
|
+
facility_id: encounter.location_id,
|
60
|
+
start_time: start_time,
|
61
|
+
end_time: end_time,
|
62
|
+
location_type: encounter.place_of_service,
|
63
|
+
location_details: encounter.location_name,
|
64
|
+
cpt_set: cpt_set)
|
65
|
+
end
|
66
|
+
|
67
|
+
def anesthesia_staffs
|
68
|
+
services.collect do |s|
|
69
|
+
AnesthesiaStaff.new(staff_id: s.provider_id, role: s.provider_type)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def anesthesia_case
|
74
|
+
AnesthesiaCase.new(anesthesia_record_id: encounter.encounter_id,
|
75
|
+
anesthesia_staff_set: anesthesia_staffs)
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
def pre_op
|
80
|
+
PreOp.new(age: encounter.patient_age, icd_set: encounter.diagnosis_codes, asa_class: encounter.asa_class)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module AQI
|
2
|
+
class AnesthesiaRecords
|
3
|
+
attr_accessor :encounters, :practice_id
|
4
|
+
|
5
|
+
def initialize(options = {})
|
6
|
+
self.encounters = options.delete(:encounters)
|
7
|
+
self.practice_id = options.delete(:practice_id)
|
8
|
+
end
|
9
|
+
|
10
|
+
def record_header
|
11
|
+
RecordHeader.new(practice_id).to_xml
|
12
|
+
end
|
13
|
+
|
14
|
+
def anesthesia_records
|
15
|
+
encounters.collect{|e| AnesthesiaRecord.new(e)}
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_xml
|
19
|
+
builder = Builder::XmlMarkup.new
|
20
|
+
builder.instruct!
|
21
|
+
builder.AnesthesiaRecords do |ars|
|
22
|
+
ars << record_header
|
23
|
+
|
24
|
+
anesthesia_records.each do |anes_record|
|
25
|
+
ars << anes_record.to_xml
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module AQI
|
2
|
+
class AnesthesiaStaff
|
3
|
+
attr_accessor :staff_id, :responsibility, :role, :sign_in, :sign_out, :notes
|
4
|
+
|
5
|
+
def self.responsibilities
|
6
|
+
["Supervisory", "Monitoring", "Administrative", "In charge", "Performing the case", "Medically responsible"]
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.roles
|
10
|
+
["MD", "CRNA", "DO", "RESIDENT", "PACU Nurse", "AA"]
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(options)
|
14
|
+
self.staff_id = options[:staff_id]
|
15
|
+
self.responsibility = validate_responsibility(options[:responsibility])
|
16
|
+
self.role = validate_role(options[:role])
|
17
|
+
self.sign_in = options[:sign_in]
|
18
|
+
self.sign_out = options[:sign_out]
|
19
|
+
self.notes = options[:notes]
|
20
|
+
end
|
21
|
+
|
22
|
+
def validate_responsibility(value)
|
23
|
+
self.class.responsibilities.include?(value) ? value : nil
|
24
|
+
end
|
25
|
+
|
26
|
+
def validate_role(value)
|
27
|
+
self.class.roles.include?(value) ? value : nil
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_xml
|
31
|
+
builder = Builder::XmlMarkup.new
|
32
|
+
builder.AnesthesiaStaff do |as|
|
33
|
+
as.StaffID(staff_id)
|
34
|
+
as.StaffResponsibility(responsibility) if responsibility
|
35
|
+
as.StaffRole(role) if role
|
36
|
+
as.StaffSignIn(sign_in.strftime('%FT%T')) if sign_in
|
37
|
+
as.StaffSignOut(sign_out.strftime('%FT%T')) if sign_out
|
38
|
+
as.StaffNotes(notes) if notes
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/lib/aqi/cpt.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
module AQI
|
2
|
+
class CPT
|
3
|
+
attr_accessor :rank, :value, :modifier
|
4
|
+
|
5
|
+
def initialize(options)
|
6
|
+
self.rank = options[:rank]
|
7
|
+
self.value = validate_value(options[:value])
|
8
|
+
self.modifier = options[:modifier]
|
9
|
+
end
|
10
|
+
|
11
|
+
def validate_value(value)
|
12
|
+
value =~ /\d\d\d\d[\w\d]/ ? value : nil
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_xml
|
16
|
+
return '' unless value
|
17
|
+
|
18
|
+
builder = Builder::XmlMarkup.new
|
19
|
+
builder.CPT do |cpt|
|
20
|
+
cpt.CPTRank(rank) if rank
|
21
|
+
cpt.CPTValue(value)
|
22
|
+
cpt.CPTModifier(modifier) if modifier
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module AQI
|
2
|
+
class Demographic
|
3
|
+
attr_accessor :date_of_birth, :postal_code, :state, :city, :race, :gender
|
4
|
+
|
5
|
+
def initialize(options)
|
6
|
+
self.date_of_birth = options[:date_of_birth]
|
7
|
+
self.postal_code = options[:postal_code]
|
8
|
+
self.state = options[:state]
|
9
|
+
self.city = options[:city]
|
10
|
+
self.race = options[:race]
|
11
|
+
self.gender = options[:gender]
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_xml
|
15
|
+
builder = Builder::XmlMarkup.new
|
16
|
+
builder.Demographic do |d|
|
17
|
+
d.DOB(date_of_birth.strftime('%F')) if date_of_birth
|
18
|
+
d.HomeZip(postal_code)
|
19
|
+
d.HomeState(state)
|
20
|
+
d.HomeCity(city)
|
21
|
+
d.Race(race || 'UNKNOWN')
|
22
|
+
d.Gender(gender)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/aqi/ic_event.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
module AQI
|
2
|
+
class ICEvent
|
3
|
+
attr_accessor :event_time_date, :category, :severity, :name, :description, :value, :notes
|
4
|
+
|
5
|
+
def self.categories
|
6
|
+
["MEDICAL DEVICE/EQUIPMENT","MEDICATION", "INFRASTRUCTURE/SYSTEM", "ASSESSMENT/DOCUMENTATION",
|
7
|
+
"RESPIRATORY/AIRWAY", "CARDIOVASCULAR", "PROCEDURE RELATED", "OTHER", "UNKNOWN" ]
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(options)
|
11
|
+
self.event_time_date = options.delete(:event_time_date)
|
12
|
+
self.category = options.delete(:category)
|
13
|
+
self.severity = options.delete(:severity)
|
14
|
+
self.name = options.delete(:name)
|
15
|
+
self.description = options.delete(:description)
|
16
|
+
self.value = options.delete(:value)
|
17
|
+
self.notes = options.delete(:notes)
|
18
|
+
end
|
19
|
+
|
20
|
+
def validate_category(value)
|
21
|
+
self.class.categories.include?(value) ? value : "UNKNOWN"
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_xml
|
25
|
+
builder = Builder::XmlMarkup.new
|
26
|
+
builder.ICEvent do |ice|
|
27
|
+
ice.ICEventTimeDate(event_time_date.strftime('%FT%T')) if event_time_date
|
28
|
+
ice.ICCategory(validate_category(category))
|
29
|
+
ice.ICSeverity(severity)
|
30
|
+
ice.ICName(name)
|
31
|
+
ice.ICDesciption(description)
|
32
|
+
ice.ICValue(value)
|
33
|
+
ice.ICNotes(notes)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class ICEventsPreparer
|
39
|
+
def self.prepare_ic_events(outcome_events)
|
40
|
+
ic_events = outcome_events.ic_events
|
41
|
+
|
42
|
+
ic_events.collect do |event|
|
43
|
+
self.ic_event.new(event)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.ic_event
|
48
|
+
ICEvent
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module AQI
|
2
|
+
class IntakeOutputSet
|
3
|
+
attr_accessor :output_direction, :output_name, :output_units, :input_output_total, :input_output_route
|
4
|
+
|
5
|
+
def initialize(options)
|
6
|
+
self.output_direction = options.delete(:output_direction)
|
7
|
+
self.output_name = options.delete(:output_name)
|
8
|
+
self.output_units = options.delete(:output_units)
|
9
|
+
self.input_output_total = options.delete(:input_output_total)
|
10
|
+
self.input_output_route = options.delete(:input_output_route)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module AQI
|
2
|
+
class IntubationDetail
|
3
|
+
attr_accessor :intubation_approach, :intubation_attempts, :tube_size, :tube_type, :intubation_details_properties_set
|
4
|
+
|
5
|
+
def self.approach_types
|
6
|
+
["Endoctracheal", "Nasogastric", "Nasotracheal", "Fiberoptic", "Tracheostomy", "Speaking tracheostomy", "OTHER", "UNKNOWN"]
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize(options)
|
10
|
+
self.intubation_approach = options.delete(:intubation_approach)
|
11
|
+
self.intubation_attempts = options.delete(:intubation_attempts)
|
12
|
+
self.tube_size = options.delete(:tube_size)
|
13
|
+
self.tube_type = options.delete(:intubation_details_properties_set) # Class
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/aqi/outcome.rb
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
module AQI
|
2
|
+
class Outcome
|
3
|
+
attr_accessor :time_date, :time_frame, :severity_code, :category_code, :sub_category_code, :location_type_code, :name, :description, :value, :range_min, :range_max, :notes
|
4
|
+
|
5
|
+
def initialize(options)
|
6
|
+
self.time_date = options.delete(:time_date)
|
7
|
+
self.time_frame = options.delete(:time_frame)
|
8
|
+
self.severity_code = options.delete(:severity_code)
|
9
|
+
self.category_code = options.delete(:category_code)
|
10
|
+
self.sub_category_code = options.delete(:sub_category_code)
|
11
|
+
self.location_type_code = options.delete(:location_type_code)
|
12
|
+
self.name = options.delete(:name)
|
13
|
+
self.description = options.delete(:description)
|
14
|
+
self.value = options.delete(:value)
|
15
|
+
self.range_min = options.delete(:range_min)
|
16
|
+
self.range_max = options.delete(:range_max)
|
17
|
+
self.notes = options.delete(:notes)
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.time_frames
|
21
|
+
["PreOp", "IntraOp", "PACU", "24h", "48h", "Week", "Month"]
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.severities
|
25
|
+
["NoEffect", "PotentialHazard", "LossOfTime", "LossOfMoney", "HarmToPatient", "SevereHarmToPatient", "PermanentDisability", "Death"]
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.categories
|
29
|
+
["PatientSurvey", "Outcome", "OTHER", "UNKNOWN"]
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.sub_categories
|
33
|
+
["MEDICAL DEVICE/EQUIPMENT", "MEDICATION", "INFRASTRUCTURE/SYSTEM", "ASSESSMENT/DOCUMENTATION", "RESPIRATORY/AIRWAY", "CARDIOVASCULAR", "PROCEDURE RELATED", "OTHER", "UNKNOWN"]
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.location_codes
|
37
|
+
["In-Patient", "Post In-Patient", "Out-Patient", "Post Out-Patient", "Obstetrics", "Surgery Center", "Surgery Center Post Out-Patient", "Office", "Home", "Ambulatory Surgical Center", "Pharmacy", "Skilled Nursing Facility", "Ambulance - Land", "Emergency Room Hospital", "Urgent Care Facility", "Inpatient Psychiatric Facility", "School", "Custodial Care Facility", "Residential Substance Abuse Treatment Facility", "Military Treatment Facility", "OTHER", "UNKNOWN"]
|
38
|
+
end
|
39
|
+
|
40
|
+
def to_xml
|
41
|
+
builder = Builder::XmlMarkup.new
|
42
|
+
builder.Outcome do |o|
|
43
|
+
o.OutcomeTimeDate(time_date.strftime('%FT%T')) if time_date
|
44
|
+
o.OutcomeTimeFrame(validate_time_frames(time_frame)) if time_frame
|
45
|
+
o.OutcomeSeverity(validate_severity(severity_code)) if severity_code
|
46
|
+
o.OutcomeCategory(validate_category(category_code)) if category_code
|
47
|
+
o.OutcomeSubcategory(validate_sub_category(sub_category_code)) if sub_category_code
|
48
|
+
o.OutcomeLocation(validate_location_code(location_type_code)) if location_type_code
|
49
|
+
o.OutcomeName(name) if name
|
50
|
+
o.OutcomeDesciption(description) if description
|
51
|
+
o.OutcomeValue(value) if value
|
52
|
+
o.OutcomeRangeMin(range_min) if range_min
|
53
|
+
o.OutcomeRangeMax(range_max) if range_max
|
54
|
+
o.OutcomeNotes(notes) if notes
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
protected
|
59
|
+
|
60
|
+
def validate_category(value)
|
61
|
+
self.class.categories.include?(value) ? value : "UNKNOWN"
|
62
|
+
end
|
63
|
+
|
64
|
+
def validate_time_frames(value)
|
65
|
+
self.class.time_frames.include?(value) ? value : ""
|
66
|
+
end
|
67
|
+
|
68
|
+
def validate_severity(value)
|
69
|
+
self.class.severities.include?(value) ? value : "NoEffect"
|
70
|
+
end
|
71
|
+
|
72
|
+
def validate_sub_category(value)
|
73
|
+
self.class.sub_categories.include?(value) ? value : "UNKNOWN"
|
74
|
+
end
|
75
|
+
|
76
|
+
def validate_location_code(value)
|
77
|
+
self.class.location_codes.include?(value) ? value : "UNKNOWN"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
class OutcomesPreparer
|
82
|
+
def self.prepare_outcomes(outcome_events)
|
83
|
+
outcomes = outcome_events.outcomes
|
84
|
+
|
85
|
+
outcomes.collect do |o|
|
86
|
+
self.outcome.new(o)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def self.outcome
|
91
|
+
Outcome
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module AQI
|
2
|
+
class OutcomeEvents
|
3
|
+
attr_accessor :ic_events, :outcomes, :event_preparer, :outcomes_preparer
|
4
|
+
def initialize(options)
|
5
|
+
@event_preparer = options.delete(:event_preparer) || AQI::ICEventsPreparer
|
6
|
+
@ic_events = options.delete(:ic_events) || []
|
7
|
+
@outcomes_preparer = options.delete(:outcomes_preparer) || AQI::OutcomesPreparer
|
8
|
+
@outcomes = options.delete(:outcomes) || []
|
9
|
+
prepare
|
10
|
+
end
|
11
|
+
|
12
|
+
def prepare
|
13
|
+
prepare_ic_events
|
14
|
+
prepare_outcomes
|
15
|
+
end
|
16
|
+
|
17
|
+
def prepare_ic_events
|
18
|
+
self.ic_events = event_preparer.prepare_ic_events(self)
|
19
|
+
end
|
20
|
+
|
21
|
+
def prepare_outcomes
|
22
|
+
self.outcomes = outcomes_preparer.prepare_outcomes(self)
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_xml
|
26
|
+
builder = Builder::XmlMarkup.new
|
27
|
+
builder.OutcomesEvents do |oe|
|
28
|
+
oe.ICEventSet do |ic_set|
|
29
|
+
self.ic_events.each do |event|
|
30
|
+
ic_set.ICEvent << event.to_xml
|
31
|
+
end
|
32
|
+
end
|
33
|
+
oe.OutcomeSet do |outcome_set|
|
34
|
+
self.outcomes.each do |outcome|
|
35
|
+
outcome_set.Outcome << outcome.to_xml
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/lib/aqi/pre_op.rb
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
class PreOp
|
2
|
+
|
3
|
+
ICD = Struct.new(:rank, :value) do
|
4
|
+
def to_xml
|
5
|
+
builder = Builder::XmlMarkup.new
|
6
|
+
builder.ICD do |icd|
|
7
|
+
icd.ICDRank(rank)
|
8
|
+
icd.ICDValue(value)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
PreRisk = Struct.new(:category, :name, :notes) do
|
14
|
+
def to_xml
|
15
|
+
builder = Builder::XmlMarkup.new
|
16
|
+
builder.PreRisk do |prs|
|
17
|
+
prs.PreOPRiskCategory(category)
|
18
|
+
prs.PreOPRiskName(name)
|
19
|
+
prs.PreOPRiskNotes(notes)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
attr_accessor :age, :weight_pounds, :height_inches, :asa_class, :pre_anesthesia_status, :icd_set, :pre_risk_set
|
25
|
+
|
26
|
+
def initialize(options)
|
27
|
+
self.age = validate_age(options.delete(:age))
|
28
|
+
self.weight_pounds = options.delete(:weight_pounds)
|
29
|
+
self.height_inches = options.delete(:height_inches)
|
30
|
+
self.asa_class = validate_asa_class(options.delete(:asa_class))
|
31
|
+
self.pre_anesthesia_status = validate_pre_anesthesia_status(options.delete(:pre_anesthesia_status))
|
32
|
+
|
33
|
+
self.icd_set = setup_icd_set(options.delete(:icd_set))
|
34
|
+
self.pre_risk_set = setup_pre_risk_set(options.delete(:pre_risk_set))
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.asa_classes
|
38
|
+
["I", "II", "III", "IV", "V", "VI", "IE", "IIE", "IIIE", "IVE", "VE", "VIE"]
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.pre_anesthesia_statuses
|
42
|
+
["Awake", "Awake", "Asleep", "Confused", "Unresponsive", "Apprehensive", "Uncooperative", "OTHER", "UNKNOWN"]
|
43
|
+
end
|
44
|
+
|
45
|
+
def height_in_cm
|
46
|
+
self.height_inches.to_f / 0.3937
|
47
|
+
end
|
48
|
+
|
49
|
+
def weight_in_kg
|
50
|
+
self.weight_pounds.to_f / 0.45359
|
51
|
+
end
|
52
|
+
|
53
|
+
def to_xml
|
54
|
+
builder = Builder::XmlMarkup.new
|
55
|
+
builder.PreOp do |pre|
|
56
|
+
pre.Age(age) if age
|
57
|
+
pre.Weight(weight_pounds) if weight_pounds
|
58
|
+
pre.WeightInKg(weight_in_kg) if weight_pounds
|
59
|
+
pre.Height(height_inches) if height_inches
|
60
|
+
pre.HeightInCm(height_in_cm) if height_inches
|
61
|
+
pre.ASAClass(asa_class) if asa_class
|
62
|
+
pre.PreAnesthStatus(pre_anesthesia_status) if pre_anesthesia_status
|
63
|
+
|
64
|
+
pre.ICDSet do |set|
|
65
|
+
icd_set.each do |icd|
|
66
|
+
set << icd.to_xml
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
pre.PreRiskSet do |set|
|
71
|
+
pre_risk_set.each do |prs|
|
72
|
+
set << prs.to_xml
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# pre.PreLabSet
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
protected
|
81
|
+
|
82
|
+
def setup_pre_risk_set(set)
|
83
|
+
return [] if set.nil?
|
84
|
+
|
85
|
+
set.collect do |args|
|
86
|
+
PreRisk.new(*args)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def setup_icd_set(set)
|
91
|
+
return [] if set.nil?
|
92
|
+
|
93
|
+
set.collect.with_index do |icd, index|
|
94
|
+
ICD.new(index + 1, icd)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def validate_age(value)
|
99
|
+
value.to_i > 0 ? value.to_i : nil
|
100
|
+
end
|
101
|
+
|
102
|
+
def validate_asa_class(value)
|
103
|
+
self.class.asa_classes.include?(value) ? value : nil # no default
|
104
|
+
end
|
105
|
+
|
106
|
+
def validate_pre_anesthesia_status(value)
|
107
|
+
self.class.pre_anesthesia_statuses.include?(value) ? value : nil
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module AQI
|
2
|
+
class Procedure
|
3
|
+
attr_accessor :procedure_id, :facility_id, :location, :location_type, :location_details,
|
4
|
+
:start_time, :end_time, :admission_status, :status, :transfer_status,
|
5
|
+
:admission_date, :notes, :cpt_set
|
6
|
+
|
7
|
+
def self.admission_statuses
|
8
|
+
%w{Emergency Inpatient Outpatient Urgent OTHER UNKNOWN}
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.statuses
|
12
|
+
%w{Emergent Urgent Elective OTHER UNKNOWN}
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.transfer_statuses
|
16
|
+
["Floor bed", "Observation unit", "Telemetry/step-down unit", "Home with services", "Died",
|
17
|
+
"Operating Room", "Intensive Care Unit", "Home without services", "Left against medical advice",
|
18
|
+
"Transferred to another hospital", "OTHER", "UNKNOWN"]
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize(options)
|
22
|
+
self.procedure_id = options[:procedure_id]
|
23
|
+
self.facility_id = options[:facility_id]
|
24
|
+
self.start_time = options[:start_time]
|
25
|
+
self.end_time = options[:end_time]
|
26
|
+
self.admission_status = validate_admission_status(options[:admission_status])
|
27
|
+
self.status = validate_status(options[:status])
|
28
|
+
self.transfer_status = validate_transfer_status(options[:transfer_status])
|
29
|
+
self.admission_date = options[:admission_date]
|
30
|
+
self.notes = options[:notes]
|
31
|
+
self.cpt_set = options[:cpt_set] || []
|
32
|
+
|
33
|
+
self.location = options[:location] || ProcedureLocation.new(type: options[:location_type],
|
34
|
+
details: options[:location_details])
|
35
|
+
end
|
36
|
+
|
37
|
+
def validate_admission_status(value)
|
38
|
+
self.class.admission_statuses.include?(value) ? value : 'UNKNOWN'
|
39
|
+
end
|
40
|
+
|
41
|
+
def validate_status(value)
|
42
|
+
self.class.statuses.include?(value) ? value : 'UNKNOWN'
|
43
|
+
end
|
44
|
+
|
45
|
+
def validate_transfer_status(value)
|
46
|
+
self.class.transfer_statuses.include?(value) ? value : 'UNKNOWN'
|
47
|
+
end
|
48
|
+
|
49
|
+
def to_xml
|
50
|
+
builder = Builder::XmlMarkup.new
|
51
|
+
builder.Procedure do |p|
|
52
|
+
p.ProcedureID(procedure_id)
|
53
|
+
p.FacilityID(facility_id)
|
54
|
+
p << location.to_xml
|
55
|
+
p.ProcStartTime(start_time.strftime('%FT%T')) if start_time
|
56
|
+
p.ProcEndTime(end_time.strftime('%FT%T')) if end_time
|
57
|
+
p.AdmissionStatus(admission_status)
|
58
|
+
p.ProcStatus(status)
|
59
|
+
p.TransferStatus(transfer_status)
|
60
|
+
p.AdmissionDate(admission_date.strftime('%FT%T')) if admission_date
|
61
|
+
p.ProcedureNotes(notes) if notes
|
62
|
+
p.CPTSet do |c|
|
63
|
+
cpt_set.each do |cpt|
|
64
|
+
c << cpt.to_xml
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module AQI
|
2
|
+
class ProcedureLocation
|
3
|
+
attr_accessor :type, :details
|
4
|
+
|
5
|
+
def self.types
|
6
|
+
["In-Patient", "Post In-Patient", "Out-Patient", "Post Out-Patient", "Obstetrics", "Surgery Center",
|
7
|
+
"Surgery Center Post Out-Patient", "Office", "Home", "Ambulatory Surgical Center", "Pharmacy",
|
8
|
+
"Skilled Nursing Facility", "Ambulance - Land", "Emergency Room Hospital", "Urgent Care Facility",
|
9
|
+
"Inpatient Psychiatric Facility", "School", "Custodial Care Facility",
|
10
|
+
"Residential Substance Abuse Treatment Facility", "Military Treatment Facility", "OTHER", "UNKNOWN"]
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(options)
|
14
|
+
self.type = validate_type_code(options[:type])
|
15
|
+
self.details = options[:details]
|
16
|
+
end
|
17
|
+
|
18
|
+
def validate_type_code(code)
|
19
|
+
if self.class.types.include?(code)
|
20
|
+
code
|
21
|
+
else
|
22
|
+
'UNKNOWN'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_xml
|
27
|
+
builder = Builder::XmlMarkup.new
|
28
|
+
builder.ProcedureLocation do |pl|
|
29
|
+
pl.LocationType(type)
|
30
|
+
pl.LocationDetails(details)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module AQI
|
2
|
+
class RecordHeader
|
3
|
+
attr_accessor :practice_id
|
4
|
+
|
5
|
+
def initialize(practice_id)
|
6
|
+
self.practice_id = practice_id
|
7
|
+
end
|
8
|
+
|
9
|
+
def to_xml
|
10
|
+
builder = Builder::XmlMarkup.new
|
11
|
+
builder.RecordHeader do |rh|
|
12
|
+
rh.PracticeID(practice_id)
|
13
|
+
rh.CreatedBy('ProMedical')
|
14
|
+
rh.CreateDate(Time.now.strftime('%FT%T'))
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/aqi/version.rb
ADDED
data/lib/aqi.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'builder'
|
2
|
+
require 'date'
|
3
|
+
require 'pry'
|
4
|
+
|
5
|
+
require_relative "aqi/version"
|
6
|
+
require_relative "aqi/record_header"
|
7
|
+
require_relative "aqi/anesthesia_records"
|
8
|
+
require_relative "aqi/anesthesia_record"
|
9
|
+
require_relative "aqi/demographic"
|
10
|
+
require_relative "aqi/procedure_location"
|
11
|
+
require_relative "aqi/procedure"
|
12
|
+
require_relative "aqi/anesthesia_case"
|
13
|
+
require_relative "aqi/anesthesia_staff"
|
14
|
+
require_relative "aqi/ic_event"
|
15
|
+
require_relative "aqi/cpt"
|
16
|
+
require_relative "aqi/anesthesia_detail"
|
17
|
+
require_relative "aqi/intubation_detail"
|
18
|
+
require_relative "aqi/intake_output_set"
|
19
|
+
require_relative "aqi/pre_op"
|
20
|
+
require_relative "aqi/outcome_events"
|
21
|
+
require_relative "aqi/outcome"
|
22
|
+
|
23
|
+
module AQI; end
|
24
|
+
|
25
|
+
if __FILE__ == $0
|
26
|
+
class NullEncounter
|
27
|
+
Service = Struct.new(:start_time, :end_time, :performing_provider)
|
28
|
+
|
29
|
+
def date_of_birth; Time.now; end
|
30
|
+
def postal_code; "34476"; end
|
31
|
+
def state; "FL"; end
|
32
|
+
def city; "Ocala"; end
|
33
|
+
def gender; "M"; end
|
34
|
+
def encounter_id; 1; end
|
35
|
+
def location; "1003"; end
|
36
|
+
def location_name; "LROR"; end
|
37
|
+
def place_of_service; "LROR"; end
|
38
|
+
def services
|
39
|
+
[Service.new(Time.now, Time.now, "Dr. Perry Platypus")]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
puts AQI::RecordHeader.new("100000").to_xml
|
44
|
+
puts AQI::AnesthesiaRecord.new(NullEncounter.new).to_xml
|
45
|
+
end
|
metadata
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: aqi
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.3
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Robert Jackson
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-02-04 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: Generates output based on the AQI schema.
|
15
|
+
email:
|
16
|
+
- robertj@promedicalinc.com
|
17
|
+
executables: []
|
18
|
+
extensions: []
|
19
|
+
extra_rdoc_files: []
|
20
|
+
files:
|
21
|
+
- .gitignore
|
22
|
+
- Gemfile
|
23
|
+
- LICENSE.txt
|
24
|
+
- README.md
|
25
|
+
- Rakefile
|
26
|
+
- aqi.gemspec
|
27
|
+
- lib/aqi.rb
|
28
|
+
- lib/aqi/anesthesia_case.rb
|
29
|
+
- lib/aqi/anesthesia_detail.rb
|
30
|
+
- lib/aqi/anesthesia_record.rb
|
31
|
+
- lib/aqi/anesthesia_records.rb
|
32
|
+
- lib/aqi/anesthesia_staff.rb
|
33
|
+
- lib/aqi/cpt.rb
|
34
|
+
- lib/aqi/demographic.rb
|
35
|
+
- lib/aqi/ic_event.rb
|
36
|
+
- lib/aqi/intake_output_set.rb
|
37
|
+
- lib/aqi/intubation_detail.rb
|
38
|
+
- lib/aqi/outcome.rb
|
39
|
+
- lib/aqi/outcome_events.rb
|
40
|
+
- lib/aqi/pre_op.rb
|
41
|
+
- lib/aqi/procedure.rb
|
42
|
+
- lib/aqi/procedure_location.rb
|
43
|
+
- lib/aqi/record_header.rb
|
44
|
+
- lib/aqi/version.rb
|
45
|
+
homepage: https://github.com/promedical/aqi
|
46
|
+
licenses: []
|
47
|
+
post_install_message:
|
48
|
+
rdoc_options: []
|
49
|
+
require_paths:
|
50
|
+
- lib
|
51
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ! '>='
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
none: false
|
57
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
none: false
|
63
|
+
requirements: []
|
64
|
+
rubyforge_project:
|
65
|
+
rubygems_version: 1.8.24
|
66
|
+
signing_key:
|
67
|
+
specification_version: 3
|
68
|
+
summary: Generates output based on the AQI schema.
|
69
|
+
test_files: []
|
70
|
+
has_rdoc:
|