Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
3c18dcd
fix art workflow
andie23 Aug 25, 2025
9f64c9e
add script to align strayed metadata
brian-mw Aug 29, 2025
7bef84f
add README
brian-mw Aug 29, 2025
08fc272
remove metadata tables from remaping
brian-mw Aug 29, 2025
3b9fda3
Migrate based data
Nov 6, 2025
ed093e2
Restored parallel processing
Nov 6, 2025
df582c5
scope models to a location
brian-mw Nov 7, 2025
587fb24
revert to location
brian-mw Nov 7, 2025
3654687
revert to location
brian-mw Nov 7, 2025
9607e20
revert to location
brian-mw Nov 7, 2025
595089b
Merge pull request #819 from EGPAFMalawiHIS/mahis_datamigration
fchiyenda Nov 11, 2025
70e7be4
Merge remote-tracking branch 'origin/sync-v5.6.6-with-mahis' into emr…
Nov 11, 2025
e22bb38
Updating migration
Nov 11, 2025
24b7d0b
Working Migration
Nov 11, 2025
e918d4a
Complete working migration script
Nov 11, 2025
a008888
EMR data migration to MAHIS
Nov 12, 2025
0c6db78
Update README on EMR Mahis Migration
Nov 12, 2025
ca5c2b8
migration to remove all dependancies of facilities table. move all fa…
brian-mw Nov 12, 2025
e0ebef1
Merge branch 'chore/find_patients_pagination' into feat/metadata-sync…
brian-mw Nov 12, 2025
bb0cbba
Fixed link to emr_mahis_migration
Nov 12, 2025
ed8fb2f
scope models to a location
brian-mw Nov 7, 2025
4964ad3
update csv locationxfacility file
brian-mw Nov 12, 2025
ce1085b
update location_id field to have default location
brian-mw Nov 13, 2025
6ce082e
Merge branch 'chore/find_patients_pagination' into emr_to_mahis_data_…
Nov 18, 2025
0839d73
Hand Pharmacy obs
Nov 19, 2025
362b97f
Merge branch 'chore/find_patients_pagination' of github.com:HISMalawi…
petroskayange Nov 19, 2025
0e75583
fixing facility
petroskayange Nov 19, 2025
029ba0c
Merge branch 'chore/find_patients_pagination' into feat/metadata-sync…
brian-mw Nov 20, 2025
8eb4697
Merge branch 'chore/find_patients_pagination' into sync-v5.6.6-with-m…
brian-mw Nov 20, 2025
36be1c4
Merge branch 'chore/find_patients_pagination' into fix/merge_location…
brian-mw Nov 20, 2025
6375080
Merge branch 'fix/merge_locations_x_facilities' of github.com:HISMala…
petroskayange Nov 20, 2025
eab9b06
modified: app/controllers/api/v1/facilities_controller.rb
dominickasanga Nov 20, 2025
93feb20
Merge branch 'facility_fix' of github.com:HISMalawi/BHT-EMR-API into …
dominickasanga Nov 20, 2025
1c9c5b4
modified: app/services/facility_service.rb
dominickasanga Nov 20, 2025
fd19835
new file: app/controllers/api/v1/location_attribute_controller.rb
dominickasanga Nov 22, 2025
656578e
modified: app/controllers/api/v1/facilities_controller.rb
dominickasanga Nov 22, 2025
b16e476
Added coordinates logic and nearby location logic
Senzenjani Nov 28, 2025
d4a72cd
Replaced usage of facility model with Location
Senzenjani Nov 28, 2025
3117d54
Merge pull request #826 from HISMalawi/fix/location-based-facility-se…
dominickasanga Nov 30, 2025
292e887
Merge pull request #825 from HISMalawi/facility_fix
dominickasanga Dec 1, 2025
b50a65d
Merge remote-tracking branch 'HISMalawi/sync-v5.6.6-with-mahis' into …
andie23 Dec 2, 2025
cac0759
Merge remote-tracking branch 'HISMalawi/feat/metadata-sync-script' in…
andie23 Dec 2, 2025
0cf830b
Merge remote-tracking branch 'HISMalawi/emr_to_mahis_data_migration' …
andie23 Dec 2, 2025
01b0951
Merge remote-tracking branch 'HISMalawi/fix/merge_locations_x_facilit…
andie23 Dec 2, 2025
0c4dd16
Updated list
andie23 Dec 10, 2025
07a321d
Add index
Dec 5, 2025
76d56e3
update csv locationxfacility file
brian-mw Nov 12, 2025
2346133
Index Tempory table for performance
Dec 10, 2025
e9ff473
Handling collation problems
Dec 12, 2025
ec982a1
Enhance facility import task with database backup and attribute handling
andie23 Dec 12, 2025
12f6d68
Refactor facility attribute handling to use named references for impr…
andie23 Dec 15, 2025
cdfb939
fix: check if mapping table exists before updating transactional tables
Senzenjani Dec 12, 2025
c6e997f
Rev: Revert to the old state of script and modify script on different…
Senzenjani Dec 12, 2025
bf70f57
Rev: Revert to the old state of script and modify script on different…
Senzenjani Dec 12, 2025
d43c145
fix: check if mapping table exists before updating transactional tables
Senzenjani Dec 12, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,11 @@ git_source(:github) { |repo| "https://github.com/#{repo}.git" }

ruby '~> 3.2.0'


# Gems for managing background jobs
gem 'activejob-uniqueness'
gem 'redis'
gem 'sidekiq'
gem 'sidekiq-cron'
gem 'redis'
gem 'activejob-uniqueness'

# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '~> 7.0.6'
Expand Down Expand Up @@ -63,7 +62,7 @@ gem 'his_emr_api_lab', '~> 2.0.9'
gem 'parallel', '~> 1.20.1'
gem 'time_difference'

gem "audited"
gem 'audited'

group :development, :test do
# Call 'byebug' anywhere in the code to stop execution and get a debugger console
Expand Down Expand Up @@ -91,7 +90,13 @@ gem 'concurrent-ruby', '= 1.3.4'
# gems for reading excel and csv files
gem 'roo', '~> 2.8'

#gem for object matching
# gem for object matching
gem 'whitesimilarity'

gem 'mongoid', '~> 9.0', '>= 9.0.6'

# gems for centralized migration
gem 'sys-cpu'
gem 'sys-filesystem'
gem 'sys-memory'
gem 'sys-proctable'
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
- [DDE](#dde)
- [Configuration](#configuration-1)
- [Enabling DDE](#enabling-dde)
- [EMR to MAHIS Migration](#emr_mahis_migration)
- [Updating Metadata](#updating-metadata)
- [EMC to POC](#emc-to-poc)
- [AIT Intergration](#ait-intergration)
Expand Down Expand Up @@ -200,6 +201,16 @@ curl -X POST -H "Authorization: AiJViSpF3spb" -H "Content-Type: application/json
}' "http://127.0.0.1:3000/api/v1/properties"
```

### EMR_Mahis_Migration

- Set database you would like to migrate into Mahis under centralized_source_db: in config/database.yml if not there copy from config/database.yml.example
- Make sure MAHIS is up to date
- Run the migrations script using:

```bash
rails r bin/emr_api_to_mahis_data_migrator.rb
```

### Updating Metadata

- Facilities
Expand Down
25 changes: 10 additions & 15 deletions app/controllers/api/v1/facilities_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -117,23 +117,18 @@ def search_params
end

def serialize_facility(facility)
{
id: facility.id,
code: facility.code,
name: facility.name,
display_name: facility.display_name,
type: facility.facility_type,
status: facility.status,
district: facility.district,
coordinates: facility.coordinates,
has_coordinates: facility.has_coordinates?,
created_at: facility.created_at,
updated_at: facility.updated_at
}
# the custom 'as_json' logic in the Location model.
facility.as_json(
include: {
location_attributes: {
only: %i[location_attribute_id attribute_type_id value_reference]
}
}
)
end

def serialize_facilities(facilities)
facilities.map { |facility| serialize_facility(facility) }
facilities.map { |facility| serialize_facility(facility) }
end
end
end
Expand Down
5 changes: 5 additions & 0 deletions app/controllers/api/v1/location_attribute_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class Api::V1::LocationAttributeController < ApplicationController
def show
render json: LocationAttribute.where(location_id: params[:id])
end
end
20 changes: 17 additions & 3 deletions app/controllers/api/v1/locations_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,35 @@ def index
tag = params[:tag]
city_village = params[:city_village]

locations = paginate(Location.order(:name))
# Eager load location_attributes to prevent N+1 queries
locations = paginate(Location.includes(:location_attributes).order(:name))

locations = locations.where('name like ?', "%#{name}%") unless name.blank?
locations = filter_locations_by_tag locations, tag if tag

locations = locations.where('city_village like ?', "%#{city_village}%") unless city_village.blank?

render json: locations
# Pass 'include' option to trigger custom as_json and define fields
render json: locations, include: {
location_attributes: {
only: %i[location_attribute_id attribute_type_id value_reference]
}
}
end

# Retrieve single location by its id
#
# GET /locations/:id
def show
render json: Facility.find_by(code: params[:id])
# Eager load location_attributes for the single record
location = Location.includes(:location_attributes).find_by(location_id: params[:id])

# Pass 'include' option to trigger custom as_json and define fields
render json: location, include: {
location_attributes: {
only: %i[location_attribute_id attribute_type_id value_reference]
}
}
end

# Retrieve the current configured facility
Expand Down
3 changes: 2 additions & 1 deletion app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,12 @@ def authenticate
end

User.current = user
Location.current = user.location
true
end

def check_location
location_id = GlobalProperty.where(property: CURRENT_LOCATION_PROPERTY).first.property_value
location_id = GlobalProperty.unscoped.where(property: CURRENT_LOCATION_PROPERTY).first.property_value
unless location_id
render json: { errors: ['Current location not set'] }, status: :service_unavailable
return false
Expand Down
21 changes: 8 additions & 13 deletions app/jobs/sync/facility_sync_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ class FacilitySyncJob < BaseSyncJob

# Sync all facilities to CouchDB
def perform(batch_size = 100)
sync_records_to_couchdb(Facility, 'facilities', batch_size)
sync_records_to_couchdb(Location, 'facilities', batch_size) do |model_class|
model_class.where(retired: false)
.where.not(city_village: [nil, ""])
end
end

private
Expand All @@ -13,26 +16,18 @@ def prepare_document(facility)
{
"type" => "facility",
"dde_activated" => false,
"facility_id" => facility.id,
"code" => facility.code,
"location_id" => facility.location_id,
"name" => facility.name,
"common" => facility.common,
"ownership" => facility.ownership,
"facility_type" => facility.facility_type,
"status" => facility.status,
"regulatory_status" => facility.regulatory_status,
"district" => facility.district,
"date_opened" => facility.date_opened&.iso8601,
"district" => facility.city_village,
"latitude" => facility.latitude,
"longitude" => facility.longitude,
"created_at" => facility.created_at&.iso8601,
"updated_at" => facility.updated_at&.iso8601,
"date_created" => facility.date_created&.iso8601,
"synced_at" => Time.current.iso8601
}
end

def generate_document_id(facility)
"facility_#{facility.id}"
"facility_#{facility.location_id}"
end
end
end
Expand Down
43 changes: 43 additions & 0 deletions app/models/concerns/Locatable.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# rubocop:disable Naming/FileName
# frozen_string_literal: true

# rubocop:disable Style/Documentation

# this class is responsible for assigning a
# site id to a record on save

# when a table has a site id attached to it,
# make sure its assigned during save
# the site_id is collected from the logged in user who belongs to a location

module Locatable
extend ActiveSupport::Concern

included do
if location_id_column?

belongs_to :location, foreign_key: :location_id, primary_key: :location_id, optional: true

default_scope { where(location_id: current_location_id) }
validates :location_id, presence: true
before_save :set_location_id

end
end

def set_location_id
self.location_id ||= current_location_id
end

class_methods do
def location_id_column?
column_names.include?('location_id')
end

def current_location_id
User.current&.location&.id || Location.current&.id
end
end
end

# rubocop:enable Style/Documentation, Naming/FileName
2 changes: 2 additions & 0 deletions app/models/drug_ingredient.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ class DrugIngredient < ActiveRecord::Base
self.table_name = 'drug_ingredient'
self.primary_keys = %i[ingredient_id concept_id]

include Locatable

belongs_to :concept, foreign_key: :concept_id
belongs_to :ingredient, foreign_key: :ingredient_id, class_name: 'Concept'

Expand Down
2 changes: 2 additions & 0 deletions app/models/drug_order.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ class DrugOrder < ApplicationRecord
self.table_name = :drug_order
self.primary_key = :order_id

include Locatable

belongs_to :drug, foreign_key: :drug_inventory_id
belongs_to :order, foreign_key: :order_id

Expand Down
2 changes: 2 additions & 0 deletions app/models/encounter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ class Encounter < VoidableRecord
self.table_name = :encounter
self.primary_key = :encounter_id

include Locatable

# before_save :before_save
after_create :after_create
after_void :after_void
Expand Down
2 changes: 2 additions & 0 deletions app/models/global_property.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,6 @@
class GlobalProperty < ApplicationRecord
self.table_name = :global_property
# self.primary_key = :property

include Locatable
end
Loading