# frozen_string_literal: true require_dependency "renalware/pathology" module Renalware module Pathology # A custom relation-like object, implementing a kaminiari-like pagination interface. # Its a query object but means to be used like a relation. If passed into a view you can # do = paginate(relation). # See ObservationsGroupedByDateTable for intended usage. # # .all() returns a jsonb hash of OBX results for each day a patient had an observation. # Only returns observations whose code matches observation_descriptions # # Example usage: # observation_descriptions = .. # rows = ObservationsGroupedByDateQuery.new( # patient: patient, # observation_descriptions: observation_descriptions, # per_page: 50, # page: 1 # ) # # Example output: # patient_id observation_date observations # ------------------------------------------ # 1 2018-02-02 {"CYA": "14"} # 1 2016-06-15 {"CMVD": "0.10"} # 1 2016-03-15 {"NA": "137", "TP": "74", "ALB": "48", "ALP": "71", ... # 1 2016-02-29 {"NA": "136", "TP": "78", "ALB": "47", "ALP": "71", ... # class ObservationsGroupedByDateQuery attr_reader :patient, :observation_descriptions, :page, :limit alias current_page page alias limit_value limit def initialize(patient:, observation_descriptions:, page: 1, per_page: 50) @patient = patient @observation_descriptions = observation_descriptions.presence || observation_descriptions_null_object @page = Integer(page) @limit = Integer(per_page) end def total_pages result = conn.execute(to_count_sql) total = result.getvalue(0, 0) (total.to_f / limit).ceil end def offset (page - 1) * limit end def all return Pathology::Observation.none if observation_descriptions.empty? conn.execute(to_paginated_sql) end private def observation_descriptions_null_object Pathology::Observation.none end # Returns path results grouped by day, and on each day, a hash results keyed by OBX code # containing an array of [result, comment]. The array and data structure is designed to # save space over the wire and in memory - we could have retuned an array of hashes with # code, result, comment keys etc. # Note in jsonb_object_agg the final ORDER BY is by ASC when you would think it would be # by DESC seeing as how we want to the latest result. However ASC works and DESC does not # and I am not not sure why yet. # # Example output: # # 2016-12-06, "{""NA"": [""139"", """"], ""EGFR"": [""83"", ""adjusted original: 77""]}" # 2016-10-20, "{""FOL"": [""6.4"", """"]}" # 2016-09-04, "{""TSH"": [""0.36"", """"]}" # ... def to_sql <<-SQL.squish select obs_req.patient_id, cast(observed_at as date) as observed_on, jsonb_object_agg(obs_desc.code, ARRAY[obs.result, obs.comment] order by observed_at asc) results from pathology_observations obs inner join pathology_observation_requests obs_req on obs.request_id = obs_req.id inner join pathology_observation_descriptions obs_desc on obs.description_id = obs_desc.id where patient_id = #{conn.quote(patient.id)} and obs.description_id in (#{observation_description_ids}) group by patient_id, observed_on order by patient_id asc, observed_on desc SQL end def to_count_sql "select count(*) from (#{to_sql}) as query" end def to_paginated_sql to_sql + " limit #{limit} offset #{offset}" end def conn ActiveRecord::Base.connection end def observation_description_ids observation_descriptions.map(&:id).join(",") end end end end