pvdgm_services 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (256) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +29 -0
  3. data/Rakefile +40 -0
  4. data/app/contexts/services/accept_hl7_message_context.rb +32 -0
  5. data/app/contexts/services/application_mds_context.rb +21 -0
  6. data/app/contexts/services/complete_sltc_registration_process_context.rb +23 -0
  7. data/app/contexts/services/create_account_mapping_context.rb +23 -0
  8. data/app/contexts/services/create_configured_account_context.rb +26 -0
  9. data/app/contexts/services/create_configured_facility_context.rb +26 -0
  10. data/app/contexts/services/create_credential_context.rb +23 -0
  11. data/app/contexts/services/create_facility_mapping_context.rb +23 -0
  12. data/app/contexts/services/create_public_key_context.rb +22 -0
  13. data/app/contexts/services/create_service_context.rb +22 -0
  14. data/app/contexts/services/create_service_definition_context.rb +26 -0
  15. data/app/contexts/services/create_sltc_registration_context.rb +24 -0
  16. data/app/contexts/services/create_third_party_context.rb +22 -0
  17. data/app/contexts/services/hl7_message_trimming_context.rb +19 -0
  18. data/app/contexts/services/invoke_service_context.rb +21 -0
  19. data/app/contexts/services/list_sltc_providers_context.rb +22 -0
  20. data/app/contexts/services/mds_file_processing_context.rb +27 -0
  21. data/app/contexts/services/mds_file_upload_context.rb +25 -0
  22. data/app/contexts/services/mds_pull_context.rb +21 -0
  23. data/app/contexts/services/monthly_service_table_cleanup_context.rb +19 -0
  24. data/app/contexts/services/notify_new_registration_context.rb +22 -0
  25. data/app/contexts/services/notify_sltc_provider_change_context.rb +19 -0
  26. data/app/contexts/services/request_sltc_baseline_context.rb +23 -0
  27. data/app/contexts/services/update_account_mapping_context.rb +23 -0
  28. data/app/contexts/services/update_configured_account_context.rb +26 -0
  29. data/app/contexts/services/update_configured_facility_context.rb +26 -0
  30. data/app/contexts/services/update_credential_context.rb +23 -0
  31. data/app/contexts/services/update_facility_mapping_context.rb +23 -0
  32. data/app/contexts/services/update_public_key_context.rb +23 -0
  33. data/app/contexts/services/update_service_context.rb +23 -0
  34. data/app/contexts/services/update_service_definition_context.rb +26 -0
  35. data/app/contexts/services/update_third_party_context.rb +23 -0
  36. data/app/controllers/services/account_mappings_controller.rb +54 -0
  37. data/app/controllers/services/adts_controller.rb +35 -0
  38. data/app/controllers/services/application_controller.rb +17 -0
  39. data/app/controllers/services/assessment_requests_controller.rb +48 -0
  40. data/app/controllers/services/available_files_controller.rb +19 -0
  41. data/app/controllers/services/configured_account_base_controller.rb +17 -0
  42. data/app/controllers/services/configured_accounts_controller.rb +59 -0
  43. data/app/controllers/services/configured_facilities_controller.rb +65 -0
  44. data/app/controllers/services/facility_mappings_controller.rb +58 -0
  45. data/app/controllers/services/mds_files_controller.rb +75 -0
  46. data/app/controllers/services/mds_pull_accounts_controller.rb +57 -0
  47. data/app/controllers/services/public_keys_controller.rb +50 -0
  48. data/app/controllers/services/service_base_controller.rb +18 -0
  49. data/app/controllers/services/service_definition_base_controller.rb +31 -0
  50. data/app/controllers/services/service_definitions_controller.rb +59 -0
  51. data/app/controllers/services/services_controller.rb +56 -0
  52. data/app/controllers/services/sltc_providers_controller.rb +15 -0
  53. data/app/controllers/services/sltc_registrations_controller.rb +74 -0
  54. data/app/controllers/services/status_masking.rb +17 -0
  55. data/app/controllers/services/third_parties_controller.rb +47 -0
  56. data/app/controllers/services/third_party_base_controller.rb +17 -0
  57. data/app/controllers/services/validation_controller.rb +17 -0
  58. data/app/helpers/services/application_helper.rb +8 -0
  59. data/app/helpers/services/assessment_request_helper.rb +19 -0
  60. data/app/helpers/services/available_files_helper.rb +27 -0
  61. data/app/helpers/services/mds_pull_accounts_helper.rb +27 -0
  62. data/app/helpers/services/sltc_registrations_helper.rb +15 -0
  63. data/app/mailers/services_mailer.rb +57 -0
  64. data/app/models/services/abaqis_mds_push.rb +51 -0
  65. data/app/models/services/account_mapping.rb +10 -0
  66. data/app/models/services/application_api.rb +19 -0
  67. data/app/models/services/assessment_request.rb +18 -0
  68. data/app/models/services/available_file.rb +73 -0
  69. data/app/models/services/configured_account.rb +26 -0
  70. data/app/models/services/configured_facility.rb +16 -0
  71. data/app/models/services/credential.rb +9 -0
  72. data/app/models/services/facility_mapping.rb +12 -0
  73. data/app/models/services/ftp_server.rb +69 -0
  74. data/app/models/services/hl7_inbound_service.rb +47 -0
  75. data/app/models/services/hl7_message.rb +27 -0
  76. data/app/models/services/isc_code.rb +15 -0
  77. data/app/models/services/isc_code_lookup.rb +75 -0
  78. data/app/models/services/mds_assessment.rb +371 -0
  79. data/app/models/services/mds_content.rb +55 -0
  80. data/app/models/services/mds_pull.rb +41 -0
  81. data/app/models/services/mds_pull_account.rb +192 -0
  82. data/app/models/services/mds_push.rb +24 -0
  83. data/app/models/services/mds_upload.rb +64 -0
  84. data/app/models/services/mds_upload_content.rb +148 -0
  85. data/app/models/services/mds_ws_response.rb +21 -0
  86. data/app/models/services/mds_ws_response_handler.rb +31 -0
  87. data/app/models/services/pcc_mds_pull.rb +77 -0
  88. data/app/models/services/provider_change.rb +26 -0
  89. data/app/models/services/public_key.rb +11 -0
  90. data/app/models/services/service.rb +20 -0
  91. data/app/models/services/service_definition.rb +37 -0
  92. data/app/models/services/service_implementation.rb +29 -0
  93. data/app/models/services/sltc_api.rb +179 -0
  94. data/app/models/services/sltc_api_exception.rb +54 -0
  95. data/app/models/services/sltc_mds_pull.rb +230 -0
  96. data/app/models/services/sltc_registration.rb +23 -0
  97. data/app/models/services/third_party.rb +18 -0
  98. data/app/roles/services/account_mapping_creator.rb +13 -0
  99. data/app/roles/services/account_mapping_updator.rb +12 -0
  100. data/app/roles/services/configured_account_creator.rb +13 -0
  101. data/app/roles/services/configured_account_updater.rb +12 -0
  102. data/app/roles/services/configured_facility_creator.rb +13 -0
  103. data/app/roles/services/configured_facility_updater.rb +12 -0
  104. data/app/roles/services/credential_creator.rb +11 -0
  105. data/app/roles/services/credential_updater.rb +20 -0
  106. data/app/roles/services/facility_mapping_creator.rb +13 -0
  107. data/app/roles/services/facility_mapping_updater.rb +11 -0
  108. data/app/roles/services/hl7_adt_message_saver.rb +15 -0
  109. data/app/roles/services/hl7_message_trimmer.rb +14 -0
  110. data/app/roles/services/mds_assessment_categorizer.rb +71 -0
  111. data/app/roles/services/mds_file_processor.rb +86 -0
  112. data/app/roles/services/mds_notifications.rb +44 -0
  113. data/app/roles/services/monthly_service_table_cleaner.rb +19 -0
  114. data/app/roles/services/new_registration_notifier.rb +11 -0
  115. data/app/roles/services/public_key_creator.rb +14 -0
  116. data/app/roles/services/public_key_updater.rb +12 -0
  117. data/app/roles/services/service_creator.rb +13 -0
  118. data/app/roles/services/service_definition_creator.rb +13 -0
  119. data/app/roles/services/service_definition_updater.rb +12 -0
  120. data/app/roles/services/service_invoker.rb +25 -0
  121. data/app/roles/services/service_updater.rb +11 -0
  122. data/app/roles/services/sltc_baseline_requestor.rb +19 -0
  123. data/app/roles/services/sltc_provider_change_notifier.rb +25 -0
  124. data/app/roles/services/sltc_provider_lister.rb +13 -0
  125. data/app/roles/services/sltc_registration_completer.rb +81 -0
  126. data/app/roles/services/sltc_registration_saver.rb +13 -0
  127. data/app/roles/services/submit_mds_file_for_processing.rb +28 -0
  128. data/app/roles/services/third_party_creator.rb +13 -0
  129. data/app/roles/services/third_party_updater.rb +12 -0
  130. data/app/utils/services/file_upload_handler.rb +33 -0
  131. data/app/utils/services/mds_upload_filters.rb +26 -0
  132. data/app/utils/services/mds_xml_file_parser.rb +104 -0
  133. data/app/utils/services/upload_file.rb +13 -0
  134. data/app/validators/isc_code_validator.rb +42 -0
  135. data/app/validators/mds_birthdate_validator.rb +37 -0
  136. data/app/validators/mds_date_validator.rb +8 -0
  137. data/app/validators/mds_integer_validator.rb +15 -0
  138. data/app/validators/mds_version_validator.rb +16 -0
  139. data/app/validators/state_code_validator.rb +9 -0
  140. data/app/views/layouts/services/application.html.erb +14 -0
  141. data/app/views/services/account_mappings/index.json.jbuilder +9 -0
  142. data/app/views/services/account_mappings/show.json.jbuilder +7 -0
  143. data/app/views/services/assessment_requests/index.json.jbuilder +11 -0
  144. data/app/views/services/available_files/index.json.jbuilder +13 -0
  145. data/app/views/services/configured_accounts/index.json.jbuilder +13 -0
  146. data/app/views/services/configured_accounts/show.json.jbuilder +11 -0
  147. data/app/views/services/configured_facilities/index.json.jbuilder +13 -0
  148. data/app/views/services/configured_facilities/show.json.jbuilder +11 -0
  149. data/app/views/services/facility_mappings/index.json.jbuilder +10 -0
  150. data/app/views/services/facility_mappings/show.json.jbuilder +8 -0
  151. data/app/views/services/mds_files/create.builder +10 -0
  152. data/app/views/services/mds_pull_accounts/index.json.jbuilder +11 -0
  153. data/app/views/services/public_keys/index.json.jbuilder +8 -0
  154. data/app/views/services/public_keys/show.json.jbuilder +6 -0
  155. data/app/views/services/service_definitions/index.json.jbuilder +17 -0
  156. data/app/views/services/service_definitions/show.json.jbuilder +16 -0
  157. data/app/views/services/services/index.json.jbuilder +6 -0
  158. data/app/views/services/services/show.json.jbuilder +4 -0
  159. data/app/views/services/sltc_providers/index.json.jbuilder +10 -0
  160. data/app/views/services/sltc_registrations/index.json.jbuilder +10 -0
  161. data/app/views/services/third_parties/index.json.jbuilder +7 -0
  162. data/app/views/services/third_parties/show.json.jbuilder +5 -0
  163. data/app/views/services_mailer/burying_job.html.erb +20 -0
  164. data/app/views/services_mailer/delaying_job.html.erb +20 -0
  165. data/app/views/services_mailer/notify_sltc_client_registration.html.erb +39 -0
  166. data/app/views/services_mailer/notify_sltc_client_registration_invalid_request_type.html.erb +8 -0
  167. data/app/views/services_mailer/notify_sltc_client_registration_request_invalid.html.erb +10 -0
  168. data/app/views/services_mailer/notify_sltc_client_registration_validation_errors.html.erb +13 -0
  169. data/app/views/services_mailer/notify_sltc_provider_changes.html.erb +26 -0
  170. data/app/views/services_mailer/notify_support_about_no_mds_pull.html.erb +6 -0
  171. data/config/routes.rb +48 -0
  172. data/db/migrate/20140102000000_create_services_engine_tables.rb +131 -0
  173. data/db/migrate/20140517184450_new_services_columns.rb +24 -0
  174. data/db/migrate/20140525142842_new_configured_provider_table.rb +18 -0
  175. data/db/migrate/20140714172442_add_error_column_to_hl7_messages.rb +5 -0
  176. data/db/migrate/20140730164152_mds_upload_tables.rb +78 -0
  177. data/db/sql_data/service_data_setup.sql +24 -0
  178. data/lib/services/engine.rb +40 -0
  179. data/lib/services/version.rb +3 -0
  180. data/lib/services.rb +4 -0
  181. data/lib/tasks/services_tasks.rake +40 -0
  182. data/spec/dummy/README.rdoc +261 -0
  183. data/spec/dummy/Rakefile +7 -0
  184. data/spec/dummy/app/assets/javascripts/application.js +15 -0
  185. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  186. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  187. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  188. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  189. data/spec/dummy/config/application.rb +60 -0
  190. data/spec/dummy/config/boot.rb +10 -0
  191. data/spec/dummy/config/database.yml +29 -0
  192. data/spec/dummy/config/environment.rb +5 -0
  193. data/spec/dummy/config/environments/development.rb +35 -0
  194. data/spec/dummy/config/environments/production.rb +68 -0
  195. data/spec/dummy/config/environments/test.rb +32 -0
  196. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  197. data/spec/dummy/config/initializers/email.rb +1 -0
  198. data/spec/dummy/config/initializers/inflections.rb +15 -0
  199. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  200. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  201. data/spec/dummy/config/initializers/session_store.rb +8 -0
  202. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  203. data/spec/dummy/config/locales/en.yml +5 -0
  204. data/spec/dummy/config/routes.rb +4 -0
  205. data/spec/dummy/config.ru +4 -0
  206. data/spec/dummy/db/schema.rb +237 -0
  207. data/spec/dummy/db/sql_data/services_isc_codes.sql +878 -0
  208. data/spec/dummy/log/test.log +3498 -0
  209. data/spec/dummy/public/404.html +26 -0
  210. data/spec/dummy/public/422.html +26 -0
  211. data/spec/dummy/public/500.html +25 -0
  212. data/spec/dummy/public/favicon.ico +0 -0
  213. data/spec/dummy/script/rails +6 -0
  214. data/spec/models/abaqis_mds_push_spec.rb +120 -0
  215. data/spec/models/available_file_spec.rb +234 -0
  216. data/spec/models/configured_account_spec.rb +39 -0
  217. data/spec/models/ftp_server_spec.rb +221 -0
  218. data/spec/models/isc_code_lookup_spec.rb +125 -0
  219. data/spec/models/isc_code_spec.rb +5 -0
  220. data/spec/models/mds_assessment_spec.rb +1070 -0
  221. data/spec/models/mds_pull_account_spec.rb +468 -0
  222. data/spec/models/mds_pull_spec.rb +48 -0
  223. data/spec/models/mds_push_spec.rb +43 -0
  224. data/spec/models/mds_ws_response_spec.rb +54 -0
  225. data/spec/models/pcc_mds_pull_spec.rb +273 -0
  226. data/spec/models/service_implementation_spec.rb +88 -0
  227. data/spec/models/sltc_api_exception_spec.rb +136 -0
  228. data/spec/models/sltc_api_spec.rb +192 -0
  229. data/spec/models/sltc_mds_pull_spec.rb +776 -0
  230. data/spec/roles/account_mapping_creator_spec.rb +40 -0
  231. data/spec/roles/account_mapping_updator_spec.rb +16 -0
  232. data/spec/roles/configured_account_creator_spec.rb +40 -0
  233. data/spec/roles/configured_account_updater_spec.rb +16 -0
  234. data/spec/roles/configured_facility_creator_spec.rb +40 -0
  235. data/spec/roles/configured_facility_updater_spec.rb +16 -0
  236. data/spec/roles/credential_creator_spec.rb +23 -0
  237. data/spec/roles/credential_updater_spec.rb +38 -0
  238. data/spec/roles/facility_mapping_creator_spec.rb +40 -0
  239. data/spec/roles/facility_mapping_updater_spec.rb +16 -0
  240. data/spec/roles/hl7_adt_message_saver_spec.rb +35 -0
  241. data/spec/roles/hl7_message_trimmer_spec.rb +31 -0
  242. data/spec/roles/monthly_service_table_cleaner_spec.rb +27 -0
  243. data/spec/roles/new_registration_notifier_spec.rb +18 -0
  244. data/spec/roles/service_ceator_spec.rb +34 -0
  245. data/spec/roles/service_definition_creator_spec.rb +40 -0
  246. data/spec/roles/service_definition_updater_spec.rb +16 -0
  247. data/spec/roles/service_invoker_spec.rb +22 -0
  248. data/spec/roles/service_updater_spec.rb +17 -0
  249. data/spec/roles/sltc_baseline_requestor_spec.rb +30 -0
  250. data/spec/roles/sltc_provider_lister_spec.rb +27 -0
  251. data/spec/roles/sltc_registration_completer_spec.rb +187 -0
  252. data/spec/roles/sltc_registration_saver_spec.rb +34 -0
  253. data/spec/roles/third_party_creator_spec.rb +34 -0
  254. data/spec/roles/third_party_updater_spec.rb +17 -0
  255. data/spec/spec_helper.rb +72 -0
  256. metadata +581 -0
@@ -0,0 +1,371 @@
1
+ module Services
2
+
3
+ class MdsAssessment < ActiveRecord::Base
4
+ include MdsAssessmentCategorizer
5
+
6
+ belongs_to :mds_upload
7
+ belongs_to :facility
8
+ belongs_to :resident
9
+ belongs_to :stay
10
+
11
+ attr_encrypted :responses, type: 'json'
12
+
13
+ VERSION1_0 = "1.00"
14
+ VERSION1_1 = "1.10"
15
+ VERSION1_1_DATE = "20120401"
16
+ CORRECTED = 1
17
+
18
+ MATCH_MAP = {
19
+ 'x0150' => 'a0200',
20
+ 'x0600a' => 'a0310a',
21
+ 'x0600b' => 'a0310b',
22
+ 'x0600c' => 'a0310c',
23
+ 'x0600d' => 'a0310d',
24
+ 'x0600f' => 'a0310f',
25
+ 'x0700a' => 'a2300',
26
+ 'x0700b' => 'a2000',
27
+ 'x0700c' => 'a1600'
28
+ }
29
+
30
+ MISSING_ATTRIBUTE_VALUES = %w{ ^ - }
31
+ CLEAN_ATTR_METHOD_REX = /^clean_(\w+)$/
32
+
33
+ # Record types
34
+ ADD_NEW_RECORD = 1
35
+ MODIFY_EXISTING_RECORD = 2
36
+ INACTIVATE_EXISTING_RECORD = 3
37
+
38
+ # These are fields that must be treated as if they are
39
+ # full-fledged attributes of the assessment. If they are
40
+ # not listed here, any access to the field will raise an
41
+ # exception from #method_missing.
42
+ VALIDATED_FIELDS = [ :itm_sbst_cd,
43
+ :fac_id,
44
+ :corrected,
45
+ :prior_isc_cd,
46
+ :state_cd,
47
+ :a0050,
48
+ :a0100b,
49
+ :a0200,
50
+ :a1600,
51
+ :a1800,
52
+ :a2000,
53
+ :a2200,
54
+ :a2300,
55
+ :a0310a,
56
+ :a0310b,
57
+ :a0310c,
58
+ :a0310d,
59
+ :a0310f,
60
+ :a0900,
61
+ :k0200a,
62
+ :k0200b,
63
+ :x0100]
64
+
65
+ CREATED = 0
66
+ PROCESSED = 1
67
+
68
+ alias_attribute :assessment_date, :reference_date
69
+
70
+ validates :responses,
71
+ presence: true
72
+
73
+ validates :fac_id,
74
+ presence: {
75
+ :message => "FAC_ID (CMS Assigned facility submission id) cannot be blank.",
76
+ if: :has_responses? }
77
+
78
+ validates :itm_sbst_cd,
79
+ presence: {
80
+ message: "ITM_SBST_CD (ISC) cannot be blank.",
81
+ if: :has_responses? },
82
+ isc_code: {
83
+ if: :has_responses? }
84
+
85
+ validates :state_cd,
86
+ presence: {
87
+ message: "Invalid state code entered in state_cd.",
88
+ if: :has_responses? },
89
+ state_code: {
90
+ if: :has_responses? }
91
+
92
+ validates :a1600,
93
+ mds_date: {
94
+ unless: :inactivation_assessment?,
95
+ if: :has_responses? }
96
+
97
+ validates :a2000,
98
+ mds_date: { if: :discharge_or_death_assessment? }
99
+
100
+ validates :a2300,
101
+ mds_date: { if: :a0310f_99? }
102
+
103
+ validates :a0900,
104
+ mds_birthdate: {
105
+ msg_invalid_characters: "Invalid birth date entered in a0900. Birth date may contain only numbers.",
106
+ msg_invalid_format: "Invalid birth date entered in a0900. Birth date must be formatted as YYYYMMDD, YYYYMM or YYYY.",
107
+ msg_invalid: "Invalid birth date entered in a0900",
108
+ if: :has_responses? }
109
+
110
+ validates :k0200a,
111
+ mds_integer: {
112
+ minimum: 0,
113
+ maximum: 99,
114
+ message: "Invalid height entered in k0200a. Height must be between 0 and 99.",
115
+ if: :has_responses? }
116
+
117
+ validates :k0200b,
118
+ mds_integer: {
119
+ minimum: 0,
120
+ maximum: 999,
121
+ message: "Invalid weight entered in k0200b. Weight must be between 0 and 999.",
122
+ if: :has_responses? }
123
+
124
+ validates :version,
125
+ mds_version: {
126
+ if: :has_responses? }
127
+
128
+ validate :twisted_discharge_dates,
129
+ if: :discharge_or_death_assessment?
130
+
131
+ before_validation :determine_reference_date, unless: :has_reference_date?
132
+ before_validation :determine_version, if: :has_reference_date?
133
+ before_validation :determine_matching_fields, if: :has_reference_date?
134
+ before_create :set_inact_corr_fields
135
+
136
+ scope :noncorrected, -> { where("corrected is null OR corrected = 0") }
137
+
138
+ scope :correctable_assessments, ->(mds_id, resident_id) {
139
+ where(resident_id: resident_id).
140
+ where("id != ?", mds_id).
141
+ where("corrected != ? or corrected IS NULL", CORRECTED) }
142
+
143
+ scope :correction_assessments_for_target, ->(mds) {
144
+ where(resident_id: mds.resident_id).
145
+ where(type_of_record: 2).
146
+ where("
147
+ (previous_reference_date = '#{mds.a2300}' AND item_subset_code = '#{mds.item_subset_code}') OR
148
+ (inact_corr_match = '#{mds.inact_corr_match}')
149
+ ".compact).
150
+ order("reference_date DESC")
151
+ }
152
+
153
+ scope :inactivation_assessments_for_target, ->(mds) {
154
+ where(resident_id: mds.resident_id).
155
+ where(item_subset_code: IscCode::INACTIVATION_CODE).
156
+ where("
157
+ (previous_reference_date = '#{mds.a2300}' AND item_subset_code = '#{mds.item_subset_code}') OR
158
+ (inact_corr_match = '#{mds.inact_corr_match}')
159
+ ".compact).
160
+ order("reference_date DESC")
161
+ }
162
+
163
+ # Paired with method_missing
164
+ def respond_to_missing?(sym, include_private=false)
165
+ VALIDATED_FIELDS.include?(sym) || (responses.present? && responses.keys.include?(sym.to_s)) || super
166
+ end
167
+
168
+ def method_missing(sym, *args)
169
+ # If an attribute is referenced as mds.clean_* (e.g. mds.clean_k0200b), the value
170
+ # we will return the value of the attribute as nil if it is one of the special
171
+ # mds values (^|-). If we do it this way, we don't have to splatter the special
172
+ # value junk all over the code base.
173
+ if sym.to_s =~ CLEAN_ATTR_METHOD_REX
174
+ return filter_value(responses.present? ? responses[$1] : nil)
175
+ end
176
+ return responses.present? ? responses[sym.to_s] : nil if VALIDATED_FIELDS.include?(sym)
177
+ return responses[sym.to_s] if responses.present? && responses.keys.include?(sym.to_s)
178
+ super
179
+ end
180
+
181
+ def unit_id
182
+ MDS_MODULE_ID
183
+ end
184
+
185
+ def has_responses?
186
+ responses.present?
187
+ end
188
+
189
+ def version1_0?
190
+ reference_date < Date.parse("20120401")
191
+ end
192
+
193
+ def version1_1?
194
+ reference_date >= Date.parse("20120401")
195
+ end
196
+
197
+ def entry_type
198
+ (clean_a1700 == "2") ? Stay::REENTRY : Stay::ADMISSION
199
+ end
200
+
201
+ def entry_date
202
+ a1600
203
+ end
204
+
205
+ def discharge_date
206
+ a2000
207
+ end
208
+
209
+ def discharge_type
210
+ clean_a2100.blank? ? nil : clean_a2100.to_i
211
+ end
212
+
213
+ def ssn
214
+ clean_a0600a || clean_x0500 || ""
215
+ end
216
+
217
+ def first_name
218
+ clean_a0500a || clean_x0200a || ""
219
+ end
220
+
221
+ def last_name
222
+ clean_a0500c || clean_x0200c || ""
223
+ end
224
+
225
+ def gender
226
+ clean_a0800 || clean_x0300 || ""
227
+ end
228
+
229
+ def comatose?
230
+ clean_b0100 == "1"
231
+ end
232
+
233
+ def inact_corr_match_fields(entry_discharge_reporting)
234
+ fields = %w{ x0150 x0600a x0600b x0600c x0600d x0600f }
235
+ case entry_discharge_reporting
236
+ when 99
237
+ fields << 'x0700a'
238
+ when 01
239
+ fields << 'x0700c'
240
+ when 10, 11, 12
241
+ fields << 'x0700b'
242
+ end
243
+ fields
244
+ end
245
+
246
+ def hash_field_values(fields, target=true)
247
+ field_values = fields.map do |f|
248
+ value_field = target ? f : MATCH_MAP[f]
249
+ "#{f}=#{responses[value_field]}"
250
+ end
251
+
252
+ # binding.pry
253
+
254
+ Digest::MD5.hexdigest(field_values.join(','))
255
+ end
256
+
257
+ private
258
+
259
+ def filter_value(value)
260
+ MISSING_ATTRIBUTE_VALUES.include?(value) ? nil : value
261
+ end
262
+
263
+ def twisted_discharge_dates
264
+ e_date = Date.safe_parse(entry_date)
265
+ d_date = Date.safe_parse(discharge_date)
266
+
267
+ return if e_date.nil? || d_date.nil?
268
+
269
+ errors.add('entry_discharge_dates', "Discharge date (a2000) must be later than or equal to entry date (a1600)") if e_date > d_date
270
+ end
271
+
272
+ def self.disp_abbr
273
+ "MDS 3.0"
274
+ end
275
+
276
+ def self.find_assessment_data(facility, filter, options={})
277
+ facility.find_matching_qcli_results(filter, options)
278
+ end
279
+
280
+ def self.find_non_interviewed_data(facility, filter, options={})
281
+ return nil
282
+ end
283
+
284
+
285
+ def self.find_flagged_results_for_qcli_or_subgroup(facility, filter, qcli, subgroup)
286
+ qcli_ids = Unit.find(7).qclis.collect(&:id).join(",")
287
+ qcli_results = facility.find_matching_qcli_results(filter, {:qcli_ids => qcli_ids})
288
+ (subgroup) ? qcli_results.select{|result| result.qcli_id == qcli.id && result.flags_subgroup_numerator?(qcli, subgroup.columns)} : qcli_results.select{|result| result.flags_numerator?(qcli)}
289
+ end
290
+
291
+ def self.initials; self.table_name.split('_')[1..-1].map{|i| i.slice(0,1)}.join; end
292
+
293
+ def self.select_latest_assessment_sql(filter, resident_ids_override=nil)
294
+ resident_ids = resident_ids_override || Resident.resident_ids_in_group_or_sample(filter)
295
+ resident_sql = (resident_ids.present?) ? "resident_id in (#{resident_ids.join(",")})" : "facility_id = #{filter.facility.id}"
296
+
297
+
298
+ sql=<<-SQL
299
+ SELECT *
300
+ FROM (#{order_latest_assessments_for_use(filter.facility.id, filter)}) mds_assmts
301
+ WHERE #{resident_sql}
302
+ SQL
303
+ sql.compact
304
+ end
305
+
306
+ def self.order_latest_assessments_for_use(facility_id, filter)
307
+
308
+ date_sql = ""
309
+ if filter[:mds_date_s].present?
310
+ date_sql = "AND date_e >= '#{filter['mds_date_s']}' AND date_e <= '#{filter.date_e}'"
311
+ elsif filter.date_s.present?
312
+ date_sql = "AND date_e >= '#{filter.date_s}' AND date_e <= '#{filter.date_e}'"
313
+ end
314
+
315
+ #date_sql = (filter.date_s.present?) ? "AND date_e >= '#{filter.date_s}' AND date_e <= '#{filter.date_e}'" : ""
316
+ #date_sql = (filter[:mds_date_s].present?) ? "AND date_e >= '#{filter['mds_date_s']}' AND date_e <= '#{filter.date_e}'" : ""
317
+ exclusion_sql = (filter[:mds_exclude_empty_pos_neg].present?) ? " AND NOT(positives LIKE '%[]%' and negatives LIKE '%[]%') " : ""
318
+ sql=<<-SQL
319
+ SELECT *
320
+ FROM
321
+ (SELECT * FROM qcli_results
322
+ WHERE facility_id = #{facility_id} #{date_sql} #{exclusion_sql}
323
+ ORDER BY date_s desc, id desc) mds_assessment_results
324
+ GROUP BY resident_id, qcli_id
325
+ SQL
326
+ sql.compact
327
+ end
328
+
329
+ def has_reference_date?
330
+ self.reference_date.present?
331
+ end
332
+
333
+ def set_inact_corr_fields
334
+ self.obra_reason = clean_a0310a
335
+ self.pps_reason = clean_a0310b
336
+ self.entry_disch_reporting = [ INACTIVATE_EXISTING_RECORD, MODIFY_EXISTING_RECORD ].include?(type_of_record) ? x0600f.to_i : a0310f
337
+ self.inact_corr_match = hash_field_values(inact_corr_match_fields(entry_disch_reporting), [ INACTIVATE_EXISTING_RECORD, MODIFY_EXISTING_RECORD ].include?(type_of_record) )
338
+ end
339
+
340
+ def determine_matching_fields
341
+ self.item_subset_code = itm_sbst_cd
342
+ self.type_of_record = (version == VERSION1_0 ? x0100 : a0050).to_i
343
+ self.previous_reference_date = a2200 if type_of_record == MODIFY_EXISTING_RECORD
344
+ end
345
+
346
+ def determine_reference_date
347
+ ref_date = entry_assessment? ? self.clean_a1600 : discharge_or_death_assessment? ? self.clean_a2000 : nil
348
+
349
+ if inactivation_assessment?
350
+ ref_date =
351
+ case self.x0600f
352
+ when "99" then self.clean_x0700a
353
+ when "01" then self.clean_x0700c
354
+ when "10", "11", "12" then self.clean_x0700b
355
+ else self.clean_x1100e
356
+ end
357
+ end
358
+
359
+ ref_date ||= self.clean_a2300
360
+ ref_date ||= self.clean_z0500b
361
+
362
+ self.reference_date = Date.safe_parse(ref_date)
363
+ end
364
+
365
+ def determine_version
366
+ self.version = self.reference_date >= Date.parse(VERSION1_1_DATE) ? VERSION1_1 : VERSION1_0
367
+ end
368
+
369
+ end
370
+
371
+ end
@@ -0,0 +1,55 @@
1
+ module Services
2
+
3
+ class MdsContent < ActiveRecord::Base
4
+ BASE_EXTRACTION_PATH = Rails.root.join('tmp').to_path
5
+
6
+ attr_encrypted :content, type: 'binary'
7
+ belongs_to :uploadable, polymorphic: true
8
+
9
+ def each_entry(&block)
10
+ each_zipped_file('**/*', &block)
11
+ end
12
+
13
+ def each_zipped_file(glob_pattern)
14
+ write_content_to_file
15
+ extract_zip_contents
16
+ Dir.glob(File.join(extracted_files_path, glob_pattern), File::FNM_CASEFOLD).sort.each do |zip_file_path|
17
+ yield zip_file_path, zip_file_path[extracted_files_path.length + 1..-1]
18
+ end
19
+
20
+ ensure
21
+ remove_content_file
22
+ remove_extracted_files
23
+ end
24
+
25
+ def content_path
26
+ @content_path ||= File.join(BASE_EXTRACTION_PATH, content_file_name)
27
+ end
28
+
29
+ def extracted_files_path
30
+ @extracted_files_path ||= File.join(BASE_EXTRACTION_PATH, "extracted_files_#{id}")
31
+ end
32
+
33
+ def extract_zip_contents
34
+ FileUtils.mkdir_p(extracted_files_path) unless File.exists?(extracted_files_path)
35
+ RecursiveUnzipper.new(content_path).extract_to(extracted_files_path)
36
+ end
37
+
38
+ def write_content_to_file
39
+ File.open(content_path, "wb") { |f| f.write(content) }
40
+ end
41
+
42
+ def remove_content_file
43
+ File.unlink(content_path) if File.exists?(content_path)
44
+ end
45
+
46
+ def remove_extracted_files
47
+ FileUtils.rm_rf(extracted_files_path) if File.exists?(extracted_files_path)
48
+ end
49
+
50
+ def content_file_name
51
+ @content_file_name ||= "composite_file_#{id}.zip"
52
+ end
53
+ end
54
+
55
+ end
@@ -0,0 +1,41 @@
1
+ module Services
2
+
3
+ class MdsPull < ServiceImplementation
4
+
5
+ #
6
+ # The async configuration options
7
+ #
8
+ ASYNC_SEND_OPTS = {
9
+ :priority => 100, # Pretty high priority; higher than MDS for sure
10
+ :time_to_run => 3600 # GIve it an hour
11
+ }
12
+
13
+ #
14
+ # Main (only public) entry point for the MDS pull service.
15
+ #
16
+ def invoke
17
+
18
+ service_definition.configured_accounts.enabled.each do | configured_account |
19
+
20
+ mds_pull_account = MdsPullAccount.create!(configured_account: configured_account)
21
+
22
+ mds_pull_account.ayl_send_opts(:process, ASYNC_SEND_OPTS)
23
+
24
+ end
25
+ end
26
+
27
+ def request_baseline(configured_account, provider_id, uploaded_after, uploaded_before)
28
+ # Do nothing by default; the derived class will handle the necessary details.
29
+ end
30
+
31
+ def identify_providers(configured_account)
32
+ # Do nothing by default. Only some service providers will provide this feature.
33
+ end
34
+
35
+ def request_assessments(configured_account)
36
+ # Do nothing by default. Only some service providers will provide this feature.
37
+ end
38
+
39
+ end
40
+
41
+ end
@@ -0,0 +1,192 @@
1
+ module Services
2
+
3
+ class MdsPullAccount < ActiveRecord::Base
4
+ include FileUtils
5
+ belongs_to :configured_account
6
+
7
+ DECAY_TIME = 1200 # 20 minutes
8
+
9
+ #
10
+ # Define all the statuses allowed for this object
11
+ #
12
+ STATUS_NEW = 0
13
+ STATUS_PROVIDERS_IDENTIFIED = 1
14
+ STATUS_ASSESSMENTS_REQUESTED = 2
15
+ STATUS_FILES_IDENTIFIED = 3
16
+ STATUS_FILES_DOWNLOADED = 4
17
+ STATUS_SUBMITTED_TO_ABAQIS = 5
18
+ STATUS_FILE_SYSTEM_CLEAN = 6
19
+ STATUS_REMOTE_CLEAN = 7
20
+
21
+ #
22
+ # Define the order of statuses for this object. The statuses
23
+ # can proceed ONLY in this order
24
+ #
25
+ PROCESSING_STATES = [ STATUS_NEW,
26
+ STATUS_PROVIDERS_IDENTIFIED,
27
+ STATUS_ASSESSMENTS_REQUESTED,
28
+ STATUS_FILES_IDENTIFIED,
29
+ STATUS_FILES_DOWNLOADED,
30
+ STATUS_SUBMITTED_TO_ABAQIS,
31
+ STATUS_FILE_SYSTEM_CLEAN,
32
+ STATUS_REMOTE_CLEAN ]
33
+
34
+ #
35
+ # Define the task to perform for each of the valid status, so if
36
+ # the current status of the object is STATUS_FILES_IDENTIFIED, then
37
+ # the :download_files method should be invoked. If the method is
38
+ # sucessful (doesn't raise an exception), the status of the object is automatically
39
+ # moved to the next valid status (from the list above).
40
+ #
41
+ STATE_MACHINE = {
42
+ STATUS_NEW => :identify_providers, #=> :identify_files_to_pull,
43
+ STATUS_PROVIDERS_IDENTIFIED => :request_assessments_to_process,
44
+ STATUS_ASSESSMENTS_REQUESTED => :identify_files_to_pull,
45
+ STATUS_FILES_IDENTIFIED => :download_files,
46
+ STATUS_FILES_DOWNLOADED => :submit_files_to_abaqis,
47
+ STATUS_SUBMITTED_TO_ABAQIS => :clean_up_file_system,
48
+ STATUS_FILE_SYSTEM_CLEAN => :clean_up_remote_server
49
+ }
50
+
51
+ has_one :service_definition, :through => :configured_account
52
+
53
+ validates :status, presence: true, inclusion: { in: PROCESSING_STATES }
54
+ validates :attempt, presence: true
55
+
56
+ def process
57
+ logger.info "Processing configured account: #{configured_account.to_rrepr}"
58
+
59
+ # Update the attempt count for the MDS Pull Account record. (how many times have we tried
60
+ # to process this record?)
61
+ update_attribute :attempt, attempt + 1
62
+
63
+ PROCESSING_STATES.each do | p_status |
64
+ perform_task p_status
65
+ end
66
+
67
+ rescue Exception => ex
68
+ logger.error "Error processing configured account: #{self.to_rrepr}: #{ex.message}\n#{ex.backtrace.join("\n")}"
69
+ if attempt > 1 && (attempt % 3 == 0)
70
+ ServicesMailer.burying_job(self).deliver
71
+ raise Ayl::Beanstalk::RequiresJobBury
72
+ else
73
+ ServicesMailer.delaying_job(self).deliver
74
+ raise Ayl::Beanstalk::RequiresJobDecay.new(DECAY_TIME)
75
+ end
76
+ end
77
+
78
+ def self.for_service_within_time_frame(service_key, from, to)
79
+ svc = Service.where(key: service_key).first
80
+ configured_account_ids = ConfiguredAccount.for_service(svc.id).enabled.pluck(:id)
81
+
82
+ self.where("configured_account_id IN (?) AND created_at >= ? AND created_at <= ?",
83
+ configured_account_ids, from, to)
84
+ end
85
+
86
+ private
87
+
88
+ def identify_providers
89
+ logger.debug "Identifying configured providers for: #{configured_account.to_rrepr}"
90
+ service_impl.identify_providers(configured_account)
91
+ end
92
+
93
+ def request_assessments_to_process
94
+ logger.debug "Requesting assessment to be processed from the third party"
95
+ service_impl.request_assessments(configured_account)
96
+ end
97
+
98
+ def identify_files_to_pull
99
+ logger.debug "Identifying files to pull for: #{configured_account.to_rrepr}"
100
+ list_available_files.each do | file_info |
101
+ logger.debug "File: #{file_info}"
102
+ configured_account.available_files.create(request_id: file_info[0],
103
+ filename: file_info[1],
104
+ status: AvailableFile::STATUS_AVAILABLE)
105
+ end
106
+ end
107
+
108
+ def list_available_files
109
+ service_impl.list_available_files(configured_account)
110
+ end
111
+
112
+ def download_files
113
+ logger.debug "Downloading files for: #{configured_account.to_rrepr}"
114
+
115
+ # Get the list of available_files that are ready for download
116
+ files_to_download = configured_account.available_files.available
117
+
118
+ # Actually perform the download of the files
119
+ downloaded_files = perform_file_downloads files_to_download
120
+
121
+ # Update the records
122
+ if downloaded_files.present?
123
+ fid_list = downloaded_files.join(",")
124
+ # Update only the files that were actually downloaded, but do it in a
125
+ # single db call.
126
+ AvailableFile.where("id IN (#{fid_list})").update_all("status = '#{AvailableFile::STATUS_DOWNLOADED}', downloaded_at = '#{Time.now.strftime("%Y-%m-%d %H:%M:%S")}'")
127
+ end
128
+ end
129
+
130
+ def perform_file_downloads(files_to_download)
131
+ service_impl.perform_file_downloads(configured_account, files_to_download)
132
+ end
133
+
134
+ def submit_files_to_abaqis
135
+ logger.debug "Submitting files to abaqis for: #{configured_account.to_rrepr}"
136
+ mds_push = AbaqisMdsPush.service_impl
137
+ configured_account.available_files.downloaded.each do | available_file |
138
+ begin
139
+ mds_push.push_mds_file(available_file)
140
+ rescue Exception => ex
141
+ logger.error "Error pushing file (#{available_file.to_rrepr})) to abaqis: #{ex.message}\n#{ex.backtrace.join("\n")}"
142
+ available_file.update_attribute :error, "Error pushing file to abaqis: #{ex.message}"
143
+ end
144
+ end
145
+ end
146
+
147
+ def clean_up_file_system
148
+ logger.debug "Cleaning up file system for: #{configured_account.to_rrepr}"
149
+ files_to_remove = configured_account.available_files.uploaded
150
+ files_to_remove.each do | available_file |
151
+ rm_rf available_file.download_path
152
+ available_file.update_attribute :status, AvailableFile::STATUS_DELETED
153
+ end
154
+
155
+ end
156
+
157
+ def clean_up_remote_server
158
+ logger.debug "Removing files from remote server for: #{configured_account.to_rrepr}"
159
+
160
+ # Actually remove the files from the server
161
+ removed_files = perform_cleanup_on_remote_server
162
+
163
+ # Update the records
164
+ if removed_files.present?
165
+ fid_list = removed_files.join(',')
166
+ # Update only the files that were actually removed, but do it in a
167
+ # single db call.
168
+ AvailableFile.where("id IN (#{fid_list})").update_all("status = '#{AvailableFile::STATUS_CLEARED}'")
169
+ end
170
+ end
171
+
172
+ def perform_cleanup_on_remote_server
173
+ service_impl.perform_cleanup_on_remote_server(configured_account)
174
+ end
175
+
176
+ def perform_task(p_status)
177
+ return unless self.status == p_status && STATE_MACHINE[p_status]
178
+
179
+ # Invoke the appropriate method for this status
180
+ send STATE_MACHINE[status]
181
+
182
+ # If the processing completes without a hitch, update the status attribute
183
+ # to the next appropriate status.
184
+ update_attribute :status, PROCESSING_STATES[PROCESSING_STATES.index(p_status) + 1]
185
+ end
186
+
187
+ def service_impl
188
+ @service_impl ||= service_definition.service_implementation
189
+ end
190
+ end
191
+
192
+ end
@@ -0,0 +1,24 @@
1
+ module Services
2
+
3
+ class MdsPush < ServiceImplementation
4
+
5
+ def invoke
6
+ end
7
+
8
+ def push_mds_file(available_file)
9
+ raise "Downloaded file does not exist: #{available_file.downloaded_file_path}" unless available_file.downloaded_file_exists?
10
+ raise "Downloaded file is not valid MDS: #{available_file.downloaded_file_path}" unless available_file.valid_mds_file?
11
+ submit_to_service(available_file)
12
+ end
13
+
14
+ def self.service_impl
15
+ nil
16
+ end
17
+
18
+ def submit_to_service(available_file)
19
+ puts "## Submitting MDS file to service"
20
+ end
21
+
22
+ end
23
+
24
+ end