Skip to content

Commit 9ceb635

Browse files
committed
SIMPLEBACK-34: Add sync API for patient scores (read-only)
- Add migration to create patient_scores table (with existence check) - Add PatientScore model with Mergeable concern - Add Api::V4::PatientScoresController with sync_to_user only - Add PatientScoreTransformer for response transformation - Add patient_score schema to Api::V4::Models - Add GET route for /patient_scores/sync - Add factory and controller spec for patient_scores (sync_to_user only)
1 parent f930813 commit 9ceb635

9 files changed

Lines changed: 173 additions & 1 deletion

File tree

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
class Api::V4::PatientScoresController < Api::V4::SyncController
2+
def sync_to_user
3+
__sync_to_user__("patient_scores")
4+
end
5+
6+
private
7+
8+
def transform_to_response(patient_score)
9+
Api::V4::PatientScoreTransformer.to_response(patient_score)
10+
end
11+
end

app/models/patient_score.rb

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
class PatientScore < ApplicationRecord
2+
include Mergeable
3+
include Discard::Model
4+
5+
belongs_to :patient, optional: true
6+
7+
validates :device_created_at, presence: true
8+
validates :device_updated_at, presence: true
9+
validates :score_type, presence: true
10+
validates :score_value, presence: true, numericality: true
11+
12+
scope :for_sync, -> { with_discarded }
13+
end

app/schema/api/v4/models.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,20 @@ def patient_attribute
117117
required: %w[id patient_id height weight created_at updated_at]}
118118
end
119119

120+
def patient_score
121+
{type: :object,
122+
properties: {
123+
id: {"$ref" => "#/definitions/uuid"},
124+
patient_id: {"$ref" => "#/definitions/uuid"},
125+
score_type: {"$ref" => "#/definitions/non_empty_string"},
126+
score_value: {type: :number},
127+
deleted_at: {"$ref" => "#/definitions/nullable_timestamp"},
128+
created_at: {"$ref" => "#/definitions/timestamp"},
129+
updated_at: {"$ref" => "#/definitions/timestamp"}
130+
},
131+
required: %w[id patient_id score_type score_value created_at updated_at]}
132+
end
133+
120134
def patient_phone_number
121135
{
122136
type: :object,
@@ -458,6 +472,8 @@ def definitions
458472
patient: patient,
459473
patient_attribute: patient_attribute,
460474
patient_attributes: Api::CommonDefinitions.array_of("patient_attribute"),
475+
patient_score: patient_score,
476+
patient_scores: Api::CommonDefinitions.array_of("patient_score"),
461477
patient_business_identifier: Api::V3::Models.patient_business_identifier,
462478
patient_business_identifiers: Api::CommonDefinitions.array_of("patient_business_identifier"),
463479
phone_number: Api::V3::Models.phone_number,
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
class Api::V4::PatientScoreTransformer < Api::V4::Transformer
2+
class << self
3+
def to_response(payload)
4+
super(payload)
5+
.merge({
6+
"score_type" => payload["score_type"],
7+
"score_value" => payload["score_value"].to_f
8+
})
9+
end
10+
11+
def from_request(payload)
12+
super(payload)
13+
.merge({
14+
"score_type" => payload["score_type"],
15+
"score_value" => payload["score_value"].to_f
16+
})
17+
end
18+
end
19+
end

config/routes.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@
7171
get "sync", to: "cvd_risks#sync_to_user"
7272
post "sync", to: "cvd_risks#sync_from_user"
7373
end
74+
75+
scope :patient_scores do
76+
get "sync", to: "patient_scores#sync_to_user"
77+
end
7478
end
7579

7680
namespace :webview do
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
class CreatePatientScores < ActiveRecord::Migration[6.1]
2+
def change
3+
unless table_exists?(:patient_scores)
4+
create_table :patient_scores, id: :uuid do |t|
5+
t.references :patient, null: false, foreign_key: true, type: :uuid
6+
t.string :score_type, null: false, limit: 100
7+
t.decimal :score_value, precision: 5, scale: 2, null: false
8+
t.datetime :device_created_at, null: false
9+
t.datetime :device_updated_at, null: false
10+
t.datetime :deleted_at
11+
12+
t.timestamps
13+
end
14+
end
15+
16+
unless index_exists?(:patient_scores, [:patient_id, :score_type])
17+
add_index :patient_scores, [:patient_id, :score_type]
18+
end
19+
20+
unless index_exists?(:patient_scores, :updated_at)
21+
add_index :patient_scores, :updated_at
22+
end
23+
end
24+
end

db/structure.sql

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1959,6 +1959,23 @@ CREATE TABLE public.patient_phone_numbers (
19591959
);
19601960

19611961

1962+
--
1963+
-- Name: patient_scores; Type: TABLE; Schema: public; Owner: -
1964+
--
1965+
1966+
CREATE TABLE public.patient_scores (
1967+
id uuid NOT NULL,
1968+
patient_id uuid NOT NULL,
1969+
score_type character varying(100) NOT NULL,
1970+
score_value numeric(5,2) NOT NULL,
1971+
device_created_at timestamp without time zone NOT NULL,
1972+
device_updated_at timestamp without time zone NOT NULL,
1973+
deleted_at timestamp without time zone,
1974+
created_at timestamp(6) without time zone NOT NULL,
1975+
updated_at timestamp(6) without time zone NOT NULL
1976+
);
1977+
1978+
19621979
--
19631980
-- Name: prescription_drugs; Type: TABLE; Schema: public; Owner: -
19641981
--
@@ -6596,6 +6613,14 @@ ALTER TABLE ONLY public.patient_phone_numbers
65966613
ADD CONSTRAINT patient_phone_numbers_pkey PRIMARY KEY (id);
65976614

65986615

6616+
--
6617+
-- Name: patient_scores patient_scores_pkey; Type: CONSTRAINT; Schema: public; Owner: -
6618+
--
6619+
6620+
ALTER TABLE ONLY public.patient_scores
6621+
ADD CONSTRAINT patient_scores_pkey PRIMARY KEY (id);
6622+
6623+
65996624
--
66006625
-- Name: patients patients_pkey; Type: CONSTRAINT; Schema: public; Owner: -
66016626
--
@@ -7699,6 +7724,20 @@ CREATE INDEX index_patient_phone_numbers_on_dnd_status ON public.patient_phone_n
76997724
CREATE INDEX index_patient_phone_numbers_on_patient_id ON public.patient_phone_numbers USING btree (patient_id);
77007725

77017726

7727+
--
7728+
-- Name: index_patient_scores_on_patient_id_and_score_type; Type: INDEX; Schema: public; Owner: -
7729+
--
7730+
7731+
CREATE INDEX index_patient_scores_on_patient_id_and_score_type ON public.patient_scores USING btree (patient_id, score_type);
7732+
7733+
7734+
--
7735+
-- Name: index_patient_scores_on_updated_at; Type: INDEX; Schema: public; Owner: -
7736+
--
7737+
7738+
CREATE INDEX index_patient_scores_on_updated_at ON public.patient_scores USING btree (updated_at);
7739+
7740+
77027741
--
77037742
-- Name: index_patient_registrations_per_day_per_facilities; Type: INDEX; Schema: public; Owner: -
77047743
--
@@ -8242,6 +8281,14 @@ ALTER TABLE ONLY public.patient_phone_numbers
82428281
ADD CONSTRAINT fk_rails_0145dd0b05 FOREIGN KEY (patient_id) REFERENCES public.patients(id);
82438282

82448283

8284+
--
8285+
-- Name: patient_scores fk_rails_0209112204; Type: FK CONSTRAINT; Schema: public; Owner: -
8286+
--
8287+
8288+
ALTER TABLE ONLY public.patient_scores
8289+
ADD CONSTRAINT fk_rails_0209112204 FOREIGN KEY (patient_id) REFERENCES public.patients(id);
8290+
8291+
82458292
--
82468293
-- Name: facility_groups fk_rails_0ba9e6af98; Type: FK CONSTRAINT; Schema: public; Owner: -
82478294
--
@@ -8802,5 +8849,6 @@ INSERT INTO "schema_migrations" (version) VALUES
88028849
('20251211154907'),
88038850
('20251215113615'),
88048851
('20251219061210'),
8805-
('20260128094448');
8852+
('20260128094448'),
8853+
('20260209112204');
88068854

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
require "rails_helper"
2+
3+
describe Api::V4::PatientScoresController, type: :controller do
4+
let(:request_user) { create(:user) }
5+
let(:request_facility_group) { request_user.facility.facility_group }
6+
let(:request_facility) { create(:facility, facility_group: request_facility_group) }
7+
let(:model) { PatientScore }
8+
9+
def create_record(options = {})
10+
facility = create(:facility, facility_group: request_facility_group)
11+
patient = create(:patient, registration_facility: facility)
12+
create(:patient_score, options.merge(patient: patient))
13+
end
14+
15+
def create_record_list(n, options = {})
16+
facility = create(:facility, facility_group_id: request_facility_group.id)
17+
patient = create(:patient, registration_facility_id: facility.id)
18+
create_list(:patient_score, n, options.merge(patient: patient))
19+
end
20+
21+
it_behaves_like "a sync controller that authenticates user requests: sync_to_user"
22+
it_behaves_like "a sync controller that audits the data access: sync_to_user"
23+
24+
describe "GET sync: send data from server to device;" do
25+
it_behaves_like "a working V3 sync controller sending records"
26+
end
27+
end

spec/factories/patient_scores.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
FactoryBot.define do
2+
factory :patient_score do
3+
id { SecureRandom.uuid }
4+
patient
5+
score_type { "risk_score" }
6+
score_value { 75.50 }
7+
device_created_at { Time.current }
8+
device_updated_at { Time.current }
9+
end
10+
end

0 commit comments

Comments
 (0)