renalware-core 2.0.0.pre.rc8 → 2.0.0.pre.rc9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/stylesheets/renalware/modules/_clinical.scss +73 -0
  3. data/app/models/concerns/renalware/clinics/most_recent_measurement_scopes.rb.dead +43 -0
  4. data/app/models/renalware/clinics/clinic_visit.rb +2 -2
  5. data/app/models/renalware/clinics/current_observations.rb +57 -0
  6. data/app/models/renalware/letters/letter.rb +5 -1
  7. data/app/models/renalware/pathology/current_observation_set.rb +6 -0
  8. data/app/models/renalware/pathology/observation.rb +1 -0
  9. data/app/models/renalware/pathology/observation_request.rb +3 -0
  10. data/app/models/renalware/pathology/observations_jsonb_serializer.rb +7 -6
  11. data/app/models/renalware/pathology/{view_current_observation_results.rb → view_current_observation_results.rb.dead} +0 -0
  12. data/app/presenters/renalware/clinical/header_presenter.rb +33 -0
  13. data/app/presenters/renalware/letters/letter_presenter.rb +5 -1
  14. data/app/presenters/renalware/letters/part_class_filter.rb +36 -0
  15. data/app/presenters/renalware/renal/clinical_summary_presenter.rb +0 -1
  16. data/app/presenters/renalware/ukrdc/patient_presenter.rb +8 -2
  17. data/app/values/renalware/bmi.rb +20 -0
  18. data/app/views/renalware/admissions/consults/_table.html.slim +1 -1
  19. data/app/views/renalware/api/ukrdc/patients/_clinic_visit_observation.xml.builder +1 -1
  20. data/app/views/renalware/api/ukrdc/patients/_lab_orders.xml.builder +3 -3
  21. data/app/views/renalware/api/ukrdc/patients/_observations.xml.builder +3 -1
  22. data/app/views/renalware/api/ukrdc/patients/lab_orders/_lab_order.xml.builder +2 -2
  23. data/app/views/renalware/api/ukrdc/patients/lab_orders/_result_item.xml.builder +7 -2
  24. data/app/views/renalware/clinical/_header.html.slim +47 -0
  25. data/app/views/renalware/clinics/clinic_visits/_table.html.slim +0 -1
  26. data/app/views/renalware/clinics/visits/_table.html.slim +4 -1
  27. data/app/views/renalware/events/events/_event.html.slim +1 -1
  28. data/app/views/renalware/events/events/_table.html.slim +3 -0
  29. data/app/views/renalware/layouts/_patient.html.slim +1 -0
  30. data/app/views/renalware/letters/letters/_form.html.slim +1 -1
  31. data/app/views/renalware/letters/letters/_pathology.html.slim +0 -1
  32. data/app/views/renalware/mdm_patients/_table.html.slim +1 -1
  33. data/app/views/renalware/pathology/_navigation.html.slim +1 -1
  34. data/app/views/renalware/patients/patients/_table.html.slim +2 -2
  35. data/config/initializers/core_extensions.rb +1 -0
  36. data/config/initializers/inflections.rb +1 -0
  37. data/config/locales/renalware/clinical/allergies.en.yml +0 -2
  38. data/config/locales/renalware/clinical/{dry_weight.yml → dry_weight.en.yml} +0 -0
  39. data/config/locales/renalware/clinical/header.en.yml +14 -0
  40. data/db/functions/audit_view_as_json_v01.sql +25 -0
  41. data/db/functions/count_estimate_v01.sql +20 -0
  42. data/db/functions/generate_patient_secure_id_v01.sql +21 -0
  43. data/db/functions/generate_secure_id_v01.sql +18 -0
  44. data/db/functions/import_gps_csv_v01.sql +129 -0
  45. data/db/functions/import_practice_memberships_csv_v01.sql +48 -0
  46. data/db/functions/import_practices_csv_v01.sql +109 -0
  47. data/db/functions/preprocess_hl7_message_v01.sql +29 -0
  48. data/db/functions/preprocess_hl7_message_v02.sql +31 -0
  49. data/db/functions/refresh_all_matierialized_views_v01.sql +32 -0
  50. data/db/functions/refresh_current_observation_set_v01.sql +39 -0
  51. data/db/functions/update_current_observation_set_from_trigger_v01.sql +88 -0
  52. data/db/functions/update_current_observation_set_from_trigger_v02.sql +90 -0
  53. data/db/migrate/20161124152732_add_deleted_at_to_patient_bookmarks.rb +1 -1
  54. data/db/migrate/20170608135553_create_functions_to_generate_secure_patient_id.rb +4 -48
  55. data/db/migrate/20170705090219_create_refresh_all_materialized_views_fn.rb +2 -37
  56. data/db/migrate/20170707110155_rename_access_plans_to_access_plan_types.rb +1 -1
  57. data/db/migrate/20170831142819_enable_crosstab_extension.rb +2 -2
  58. data/db/migrate/20170911133224_add_type_to_messaging_messages.rb +1 -1
  59. data/db/migrate/20170915115228_add_schedule_diurnal_period_id_to_hd_profiles.rb +1 -1
  60. data/db/migrate/20171013145849_set_patients_secure_id.rb +1 -1
  61. data/db/migrate/20171101121130_create_function_to_render_audit_view_as_json.rb +2 -29
  62. data/db/migrate/20171127092158_create_function_to_import_practices.rb +4 -118
  63. data/db/migrate/20171127092359_create_fn_to_insert_gps.rb +3 -139
  64. data/db/migrate/20171206140738_create_fn_to_load_practice_memberships_csv.rb +2 -53
  65. data/db/migrate/20171213111513_create_fn_to_refresh_current_obs.rb +2 -43
  66. data/db/migrate/20171214141335_create_trigger_to_update_current_observation_sets.rb +3 -101
  67. data/db/migrate/20180119121243_create_trigger_to_preprocess_hl7_msg.rb +2 -41
  68. data/db/migrate/20180121115246_add_include_pathology_in_letter_to_letters_letterheads.rb +5 -0
  69. data/db/migrate/20180125201356_make_obs_set_trigger_change_updated_at.rb +2 -184
  70. data/db/migrate/20180130165803_add_deleted_at_indexes.rb +16 -0
  71. data/db/migrate/20180201090444_add_created_at_to_delayed_jobs_in_hl7_trig_fn.rb +9 -0
  72. data/db/migrate/20180207082540_create_count_estimate_function.rb +9 -0
  73. data/db/triggers/feed_messages_preprocessing_trigger_v01.sql +5 -0
  74. data/db/triggers/update_current_observation_set_trigger_v01.sql +7 -0
  75. data/lib/core_extensions/active_record/migration_helpers.rb +43 -0
  76. data/lib/renalware/engine.rb +6 -4
  77. data/lib/renalware/version.rb +1 -1
  78. data/spec/factories/pathology/observation_descriptions.rb +7 -1
  79. data/spec/support/shared_examples/accountable_examples.rb +6 -0
  80. data/spec/support/shared_examples/supersedable_examples.rb +12 -0
  81. metadata +33 -10
  82. data/app/models/renalware/letters/delivery/deliver_letter.rb.dead +0 -41
  83. data/app/models/renalware/pathology/current_key_observation_set.rb.dead +0 -10
  84. data/app/models/renalware/pathology/update_current_observations.rb.dead +0 -25
  85. data/app/presenters/renalware/pathology/current_observation_results.dead/presenter.rb.dead +0 -54
  86. data/app/views/renalware/letters/formatted_letters/show.rtf.slim.ol +0 -1
  87. data/app/views/renalware/patients/_prescriptions.html.slim.dead +0 -23
@@ -1,61 +1,10 @@
1
1
  class CreateFnToLoadPracticeMembershipsCSV < ActiveRecord::Migration[5.1]
2
2
  def up
3
- sql = <<-SQL
4
- CREATE OR REPLACE FUNCTION renalware.import_practice_memberships_csv(file text) RETURNS void
5
- AS $$
6
- BEGIN
7
-
8
- DROP TABLE IF EXISTS memberships_via_copy;
9
- CREATE TEMP TABLE copied_memberships (
10
- gp_code text NOT NULL,
11
- practice_code text NOT NULL,
12
- unused3 text,
13
- unused4 text,
14
- unused5 text,
15
- unused7 text
16
- );
17
-
18
- -- Import the CSV file into copied_memberships - note there is no CSV header in this file
19
- EXECUTE format ('COPY copied_memberships FROM %L DELIMITER %L CSV ', file, ',');
20
-
21
- DROP TABLE IF EXISTS tmp_memberships;
22
- CREATE TEMP TABLE tmp_memberships AS
23
- SELECT
24
- gp_code,
25
- practice_code,
26
- patient_primary_care_physicians.id primary_care_physician_id,
27
- patient_practices.id as practice_id
28
- from copied_memberships
29
- INNER JOIN patient_practices on patient_practices.code = practice_code
30
- INNER JOIN patient_primary_care_physicians on patient_primary_care_physicians.code = gp_code;
31
-
32
- -- Insert any new memberships, ignoring any conflicts where the
33
- -- practice_id + primary_care_physician_id already exists
34
- INSERT INTO renalware.patient_practice_memberships
35
- (practice_id, primary_care_physician_id, created_at, updated_at)
36
- SELECT
37
- practice_id,
38
- primary_care_physician_id,
39
- CURRENT_TIMESTAMP,
40
- CURRENT_TIMESTAMP
41
- FROM tmp_memberships
42
- ON CONFLICT (practice_id, primary_care_physician_id) DO NOTHING;
43
-
44
- -- Mark as deleted any memberships not in the latest uploaded data set - ie those gps have retired or moved on
45
- UPDATE patient_practice_memberships mem
46
- SET deleted_at = CURRENT_TIMESTAMP
47
- WHERE NOT EXISTS (select 1 FROM tmp_memberships tmem
48
- WHERE tmem.practice_id = mem.practice_id AND tmem.primary_care_physician_id = mem.primary_care_physician_id);
49
-
50
- END;
51
- $$ LANGUAGE plpgsql;
52
- SQL
53
-
54
- ActiveRecord::Base.connection.execute(sql)
3
+ load_function("import_practice_memberships_csv_v01.sql")
55
4
  end
56
5
 
57
6
  def down
58
- ActiveRecord::Base.connection.execute(
7
+ connection.execute(
59
8
  "DROP FUNCTION IF EXISTS import_practice_memberships_csv(file text);"
60
9
  )
61
10
  end
@@ -1,50 +1,9 @@
1
1
  class CreateFnToRefreshCurrentObs < ActiveRecord::Migration[5.1]
2
2
  def up
3
- sql = <<-SQL
4
- CREATE OR REPLACE FUNCTION refresh_current_observation_set(a_patient_id integer)
5
- -- Function to update the pathology_current_observation_set for a patient.
6
- -- It stores the most recent results into the jsonb hash on that table.
7
- -- To run for all patients, use
8
- -- select refresh_current_observation_set(id) from patients;
9
- --
10
- RETURNS integer
11
- LANGUAGE 'plpgsql'
12
- as $$
13
- BEGIN
14
- with current_patient_obs as(
15
- select
16
- DISTINCT ON (p.id, obxd.id)
17
- p.id as patient_id,
18
- obxd.code,
19
- json_build_object('result',(obx.result),'observed_at',obx.observed_at) as value
20
- from patients p
21
- inner join pathology_observation_requests obr on obr.patient_id = p.id
22
- inner join pathology_observations obx on obx.request_id = obr.id
23
- inner join pathology_observation_descriptions obxd on obx.description_id = obxd.id
24
- where p.id = a_patient_id
25
- order by p.id, obxd.id, obx.observed_at desc
26
- ),
27
- current_patient_obs_as_jsonb as (
28
- select patient_id,
29
- jsonb_object_agg(code, value) as values,
30
- CURRENT_TIMESTAMP,
31
- CuRRENT_TIMESTAMP
32
- from current_patient_obs
33
- group by patient_id order by patient_id
34
- )
35
- insert into pathology_current_observation_sets (patient_id, values, created_at, updated_at)
36
- select * from current_patient_obs_as_jsonb
37
- ON conflict (patient_id)
38
- DO UPDATE
39
- SET values = excluded.values, updated_at = excluded.updated_at;
40
- RETURN a_patient_id;
41
- END
42
- $$;
43
- SQL
44
- ActiveRecord::Base.connection.execute(sql)
3
+ load_function("refresh_current_observation_set_v01.sql")
45
4
  end
46
5
 
47
6
  def down
48
- ActiveRecord::Base.connection.execute("drop function if exists refresh_current_observation_set(integer)")
7
+ connection.execute("drop function if exists refresh_current_observation_set(integer)")
49
8
  end
50
9
  end
@@ -1,109 +1,11 @@
1
1
  class CreateTriggerToUpdateCurrentObservationSets < ActiveRecord::Migration[5.1]
2
2
  def up
3
- sql = <<-SQL
4
- CREATE OR REPLACE FUNCTION update_current_observation_set_from_trigger() RETURNS TRIGGER AS $body$
5
- -- TC 14/12/2017
6
- -- This function is called by a trigger when a row is inserted or updated in
7
- -- pathology_observations. Its purpose is to keep current_observation_sets up to date
8
- -- with the latest observations for any patient.
9
- -- The current_observation_sets table maintains a jsonb hash into which we insert or replace
10
- -- the observation, keyed by OBX code.
11
- -- e.g. .. {"HGB": { "result": 123.1, "observed_at": '2017-12-12-01:01:01'}, ..
12
- DECLARE
13
- a_patient_id bigint;
14
- a_code text;
15
- current_observed_at timestamp;
16
- current_result text;
17
- new_observed_at timestamp;
18
- BEGIN
19
- RAISE NOTICE 'TRIGGER called on %',TG_TABLE_NAME ;
20
-
21
- /*
22
- If inserting or updating, we _could_ assume the last observation to be inserted is
23
- the most 'recent' one (with the latest observed_at date).
24
- However the order of incoming messages is not guaranteed, so we have two options:
25
- 1. Refresh the entire current_observation_set for the patient
26
- 2. Check the current observed_at date in the jsonb and only update if we have a more
27
- recent one
28
- We have gone for 2.
29
- */
30
-
31
- IF (TG_OP = 'INSERT' OR TG_OP = 'UPDATE') THEN
32
-
33
- -- Note we could re-generate the entire current pathology for the patient using
34
- -- select refresh_current_observation_set(a_patient_id);
35
- -- which is safer but uses more resources, so avoiding this for now.
36
-
37
- -- Find and store patient_id into local variable
38
- select request.patient_id into a_patient_id
39
- from pathology_observation_requests request
40
- where request.id = NEW.request_id;
41
-
42
- -- Find and store the obx code into local variable
43
- select description.code into a_code
44
- from pathology_observation_descriptions description
45
- where description.id = NEW.description_id;
46
-
47
- -- Important! Create the observation_set if it doesn exist yet
48
- -- ignore the error id the row already exists
49
- insert into pathology_current_observation_sets (patient_id)
50
- values (a_patient_id)
51
- ON CONFLICT DO NOTHING;
52
-
53
- -- We are going to compare the current and new observed_at dates
54
- -- so need to cast them to a timestamp
55
- select cast(New.observed_at as timestamp) into new_observed_at;
56
-
57
- -- Get the most recent date and value for this observation
58
- -- and store to variables.
59
- select
60
- cast(values -> a_code ->> 'observed_at' as timestamp),
61
- values -> a_code ->> 'result'
62
- into current_observed_at, current_result from
63
- pathology_current_observation_sets
64
- where patient_id = a_patient_id;
65
-
66
- -- Output some info to helps us debug. This can be removed later.
67
- RAISE NOTICE ' Request id % Patient id % Code %', NEW.request_id, a_patient_id, a_code;
68
- RAISE NOTICE ' Last %: % at %', a_code, current_result, current_observed_at;
69
- RAISE NOTICE ' New %: % at %', a_code, NEW.result, new_observed_at;
70
-
71
- IF current_observed_at IS NULL OR new_observed_at >= current_observed_at THEN
72
- -- The new pathology_observation row contain a more recent result that the old one.
73
- -- (note there may not be an old one if the patient has neve had this obs before).
74
-
75
- RAISE NOTICE ' Updating pathology_current_observation_sets..';
76
-
77
- -- Update the values jsonb column with the new hash for this code, e.g.
78
- -- .. {"HGB": { "result": 123.1, "observed_at": '2017-12-12-01:01:01'}, ..
79
- -- Note the `set values` below actually reads in the jsonb, updates it,
80
- -- and wites the whole thing back.
81
- update pathology_current_observation_sets
82
- set values = jsonb_set(
83
- values,
84
- ('{'||a_code||'}')::text[], -- defined in the fn path::text[]
85
- jsonb_build_object('result', NEW.result, 'observed_at', new_observed_at),
86
- true)
87
- where patient_id = a_patient_id;
88
- END IF;
89
- END IF;
90
- RETURN NULL ;
91
- END $body$ LANGUAGE plpgsql VOLATILE COST 100;
92
- -- End function
93
-
94
- -- Create the trigger which will call out function every time a row in pathology_observations
95
- -- is inserted or updated
96
- DROP TRIGGER IF EXISTS update_current_observation_set_trigger ON pathology_observations;
97
- CREATE TRIGGER update_current_observation_set_trigger
98
- AFTER INSERT OR UPDATE
99
- ON pathology_observations
100
- FOR EACH ROW EXECUTE PROCEDURE update_current_observation_set_from_trigger();
101
- SQL
102
- ActiveRecord::Base.connection.execute(sql)
3
+ load_function("update_current_observation_set_from_trigger_v01.sql")
4
+ load_trigger("update_current_observation_set_trigger_v01.sql")
103
5
  end
104
6
 
105
7
  def down
106
- ActiveRecord::Base.connection.execute("
8
+ connection.execute("
107
9
  DROP TRIGGER IF EXISTS update_current_observation_set_trigger ON pathology_observations;
108
10
  DROP FUNCTION IF EXISTS update_current_observation_set_from_trigger();
109
11
  ")
@@ -1,46 +1,7 @@
1
1
  class CreateTriggerToPreprocessHL7Msg < ActiveRecord::Migration[5.1]
2
2
  def up
3
- # Note that we need to use a quoted heredoc ('SQL') here in order to preserve our
4
- # SQL escaping e.g. E'\\S\\' which would otherwise be interpreted by Ruby as E'\S\'
5
- sql = <<-'SQL'
6
- /* Create a function for the trigger to call */
7
- CREATE FUNCTION preprocess_hl7_message() RETURNS trigger AS
8
- $body$
9
- BEGIN
10
- /*
11
- Mirth inserts a row into delayed job when a new HL7 message needs to be processed by Renalware.
12
- The SQL it uses looks like this:
13
- insert into renalware.delayed_jobs (handler, run_at)
14
- values(E'--- !ruby/struct:FeedJob\nraw_message: |\n ' || REPLACE(${message.rawData},E'\r',E'\n '), NOW());
15
- This works unless there is a 10^12 value in the unit of measurement segment for an OBX (e.g.
16
- for WBC or HGB). Then Mirth encodes the ^ as \S\ because ^ is a significant character in Mirth
17
- (field separator). Unfortunately this creates the combination
18
- 10\S\12 and S\12 is converted to \n when the handler's payload is loaded in by the delayed_job worker.
19
- To get around this we need to convert instances of \S\ with another escape sequence eg «
20
- and manually map this back to a ^ in the job handler ruby code.
21
-
22
- So here, if this delayed_job is destined to be picked up by a Feed job handler
23
- make sure we convert the Mirth escape sequence \S\ to \\S\\
24
- */
25
- IF position('Feed' in NEW.handler) > 0 THEN
26
- NEW.handler = replace(NEW.handler, E'\\S\\', E'\\\\S\\\\');
27
- END IF;
28
-
29
- RETURN NEW;
30
- END
31
-
32
- $body$
33
- LANGUAGE plpgsql;
34
-
35
- /* Create the trigger! */
36
- CREATE TRIGGER feed_messages_preprocessing_trigger
37
- BEFORE INSERT
38
- ON delayed_jobs
39
- FOR EACH ROW
40
- EXECUTE PROCEDURE preprocess_hl7_message();
41
- SQL
42
-
43
- connection.execute(sql)
3
+ load_function("preprocess_hl7_message_v01.sql")
4
+ load_trigger("feed_messages_preprocessing_trigger_v01.sql")
44
5
  end
45
6
 
46
7
  def down
@@ -0,0 +1,5 @@
1
+ class AddIncludePathologyInLetterToLettersLetterheads < ActiveRecord::Migration[5.1]
2
+ def change
3
+ add_column :letter_letterheads, :include_pathology_in_letter_body, :boolean, default: true
4
+ end
5
+ end
@@ -1,191 +1,9 @@
1
1
  class MakeObsSetTriggerChangeUpdatedAt < ActiveRecord::Migration[5.1]
2
2
  def up
3
- sql = <<-SQL
4
- CREATE OR REPLACE FUNCTION update_current_observation_set_from_trigger() RETURNS TRIGGER AS $body$
5
- -- TC 14/12/2017
6
- -- This function is called by a trigger when a row is inserted or updated in
7
- -- pathology_observations. Its purpose is to keep current_observation_sets up to date
8
- -- with the latest observations for any patient.
9
- -- The current_observation_sets table maintains a jsonb hash into which we insert or replace
10
- -- the observation, keyed by OBX code.
11
- -- e.g. .. {"HGB": { "result": 123.1, "observed_at": '2017-12-12-01:01:01'}, ..
12
- DECLARE
13
- a_patient_id bigint;
14
- a_code text;
15
- current_observed_at timestamp;
16
- current_result text;
17
- new_observed_at timestamp;
18
- BEGIN
19
- RAISE NOTICE 'TRIGGER called on %',TG_TABLE_NAME ;
20
-
21
- /*
22
- If inserting or updating, we _could_ assume the last observation to be inserted is
23
- the most 'recent' one (with the latest observed_at date).
24
- However the order of incoming messages is not guaranteed, so we have two options:
25
- 1. Refresh the entire current_observation_set for the patient
26
- 2. Check the current observed_at date in the jsonb and only update if we have a more
27
- recent one
28
- We have gone for 2.
29
- */
30
-
31
- IF (TG_OP = 'INSERT' OR TG_OP = 'UPDATE') THEN
32
-
33
- -- Note we could re-generate the entire current pathology for the patient using
34
- -- select refresh_current_observation_set(a_patient_id);
35
- -- which is safer but uses more resources, so avoiding this for now.
36
-
37
- -- Find and store patient_id into local variable
38
- select request.patient_id into a_patient_id
39
- from pathology_observation_requests request
40
- where request.id = NEW.request_id;
41
-
42
- -- Find and store the obx code into local variable
43
- select description.code into a_code
44
- from pathology_observation_descriptions description
45
- where description.id = NEW.description_id;
46
-
47
- -- Important! Create the observation_set if it doesn't exist yet
48
- -- ignore the error if the row already exists
49
- insert into pathology_current_observation_sets (patient_id)
50
- values (a_patient_id)
51
- ON CONFLICT DO NOTHING;
52
-
53
- -- We are going to compare the current and new observed_at dates
54
- -- so need to cast them to a timestamp
55
- select cast(New.observed_at as timestamp) into new_observed_at;
56
-
57
- -- Get the most recent date and value for this observation
58
- -- and store to variables.
59
- select
60
- cast(values -> a_code ->> 'observed_at' as timestamp),
61
- values -> a_code ->> 'result'
62
- into current_observed_at, current_result from
63
- pathology_current_observation_sets
64
- where patient_id = a_patient_id;
65
-
66
- -- Output some info to helps us debug. This can be removed later.
67
- RAISE NOTICE ' Request id % Patient id % Code %', NEW.request_id, a_patient_id, a_code;
68
- RAISE NOTICE ' Last %: % at %', a_code, current_result, current_observed_at;
69
- RAISE NOTICE ' New %: % at %', a_code, NEW.result, new_observed_at;
70
-
71
- IF current_observed_at IS NULL OR new_observed_at >= current_observed_at THEN
72
- -- The new pathology_observation row contain a more recent result that the old one.
73
- -- (note there may not be an old one if the patient has neve had this obs before).
74
-
75
- RAISE NOTICE ' Updating pathology_current_observation_sets..';
76
-
77
- -- Update the values jsonb column with the new hash for this code, e.g.
78
- -- .. {"HGB": { "result": 123.1, "observed_at": '2017-12-12-01:01:01'}, ..
79
- -- Note the `set values` below actually reads in the jsonb, updates it,
80
- -- and wites the whole thing back.
81
- update pathology_current_observation_sets
82
- set updated_at = CURRENT_TIMESTAMP,
83
- values = jsonb_set(
84
- values,
85
- ('{'||a_code||'}')::text[], -- defined in the fn path::text[]
86
- jsonb_build_object('result', NEW.result, 'observed_at', new_observed_at),
87
- true)
88
- where patient_id = a_patient_id;
89
- END IF;
90
- END IF;
91
- RETURN NULL ;
92
- END $body$ LANGUAGE plpgsql VOLATILE COST 100;
93
- -- End function
94
- SQL
95
- ActiveRecord::Base.connection.execute(sql)
3
+ load_function("update_current_observation_set_from_trigger_v02.sql")
96
4
  end
97
5
 
98
6
  def down
99
- sql = <<-SQL
100
- CREATE OR REPLACE FUNCTION update_current_observation_set_from_trigger() RETURNS TRIGGER AS $body$
101
- -- TC 14/12/2017
102
- -- This function is called by a trigger when a row is inserted or updated in
103
- -- pathology_observations. Its purpose is to keep current_observation_sets up to date
104
- -- with the latest observations for any patient.
105
- -- The current_observation_sets table maintains a jsonb hash into which we insert or replace
106
- -- the observation, keyed by OBX code.
107
- -- e.g. .. {"HGB": { "result": 123.1, "observed_at": '2017-12-12-01:01:01'}, ..
108
- DECLARE
109
- a_patient_id bigint;
110
- a_code text;
111
- current_observed_at timestamp;
112
- current_result text;
113
- new_observed_at timestamp;
114
- BEGIN
115
- RAISE NOTICE 'TRIGGER called on %',TG_TABLE_NAME ;
116
-
117
- /*
118
- If inserting or updating, we _could_ assume the last observation to be inserted is
119
- the most 'recent' one (with the latest observed_at date).
120
- However the order of incoming messages is not guaranteed, so we have two options:
121
- 1. Refresh the entire current_observation_set for the patient
122
- 2. Check the current observed_at date in the jsonb and only update if we have a more
123
- recent one
124
- We have gone for 2.
125
- */
126
-
127
- IF (TG_OP = 'INSERT' OR TG_OP = 'UPDATE') THEN
128
-
129
- -- Note we could re-generate the entire current pathology for the patient using
130
- -- select refresh_current_observation_set(a_patient_id);
131
- -- which is safer but uses more resources, so avoiding this for now.
132
-
133
- -- Find and store patient_id into local variable
134
- select request.patient_id into a_patient_id
135
- from pathology_observation_requests request
136
- where request.id = NEW.request_id;
137
-
138
- -- Find and store the obx code into local variable
139
- select description.code into a_code
140
- from pathology_observation_descriptions description
141
- where description.id = NEW.description_id;
142
-
143
- -- Important! Create the observation_set if it doesn exist yet
144
- -- ignore the error id the row already exists
145
- insert into pathology_current_observation_sets (patient_id)
146
- values (a_patient_id)
147
- ON CONFLICT DO NOTHING;
148
-
149
- -- We are going to compare the current and new observed_at dates
150
- -- so need to cast them to a timestamp
151
- select cast(New.observed_at as timestamp) into new_observed_at;
152
-
153
- -- Get the most recent date and value for this observation
154
- -- and store to variables.
155
- select
156
- cast(values -> a_code ->> 'observed_at' as timestamp),
157
- values -> a_code ->> 'result'
158
- into current_observed_at, current_result from
159
- pathology_current_observation_sets
160
- where patient_id = a_patient_id;
161
-
162
- -- Output some info to helps us debug. This can be removed later.
163
- RAISE NOTICE ' Request id % Patient id % Code %', NEW.request_id, a_patient_id, a_code;
164
- RAISE NOTICE ' Last %: % at %', a_code, current_result, current_observed_at;
165
- RAISE NOTICE ' New %: % at %', a_code, NEW.result, new_observed_at;
166
-
167
- IF current_observed_at IS NULL OR new_observed_at >= current_observed_at THEN
168
- -- The new pathology_observation row contain a more recent result that the old one.
169
- -- (note there may not be an old one if the patient has neve had this obs before).
170
-
171
- RAISE NOTICE ' Updating pathology_current_observation_sets..';
172
-
173
- -- Update the values jsonb column with the new hash for this code, e.g.
174
- -- .. {"HGB": { "result": 123.1, "observed_at": '2017-12-12-01:01:01'}, ..
175
- -- Note the `set values` below actually reads in the jsonb, updates it,
176
- -- and wites the whole thing back.
177
- update pathology_current_observation_sets
178
- set values = jsonb_set(
179
- values,
180
- ('{'||a_code||'}')::text[], -- defined in the fn path::text[]
181
- jsonb_build_object('result', NEW.result, 'observed_at', new_observed_at),
182
- true)
183
- where patient_id = a_patient_id;
184
- END IF;
185
- END IF;
186
- RETURN NULL ;
187
- END $body$ LANGUAGE plpgsql VOLATILE COST 100;
188
- SQL
189
- ActiveRecord::Base.connection.execute(sql)
7
+ load_function("update_current_observation_set_from_trigger_v01.sql")
190
8
  end
191
9
  end