Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate contact's metadata into native columns #1670

Merged
merged 7 commits into from
Jan 8, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions app/controllers/api/v1/addresses_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ def index
def create
validate_request_schema(
with: ::V1::BeneficiaryAddressRequestSchema,
schema_options: { beneficiary: },
location: ->(resource) { api_v1_beneficiary_address_path(beneficiary, resource) }
) do |permitted_params|
beneficiary.addresses.create!(permitted_params)
Expand Down
5 changes: 4 additions & 1 deletion app/controllers/api/v1/beneficiaries_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ def create
# TODO: can remove this once after we rename the model to beneficiary
location: ->(resource) { api_v1_beneficiary_path(resource) }
) do |permitted_params|
CreateBeneficiaryWithAddress.new(permitted_params).call
CreateBeneficiaryWithAddress.new(
account: current_account,
**permitted_params
).call
end
end

Expand Down
4 changes: 4 additions & 0 deletions app/models/beneficiary_address.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
class BeneficiaryAddress < ApplicationRecord
extend Enumerize

enumerize :iso_country_code, in: ISO3166::Country.codes.freeze

belongs_to :beneficiary, class_name: "Contact"
end
2 changes: 2 additions & 0 deletions app/models/call_flow_logic/ews_laos_registration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -240,8 +240,10 @@ def update_contact
district = DISTRICTS.find { |d| d.code == phone_call_metadata(:district_code) }

contact.addresses.find_or_create_by!(
iso_country_code: "LA",
iso_region_code: district.province.iso3166,
administrative_division_level_2_code: district.code,
administrative_division_level_2_name: district.name_en,
)

registered_districts = contact.metadata.fetch("registered_districts", [])
Expand Down
5 changes: 4 additions & 1 deletion app/models/call_flow_logic/ews_registration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -369,9 +369,12 @@ def update_contact
commune = Pumi::Commune.find_by_id(phone_call_metadata(:commune_code))

contact.addresses.find_or_create_by!(
iso_country_code: "KH",
iso_region_code: commune.province.iso3166_2,
administrative_division_level_2_code: commune.district_id,
administrative_division_level_3_code: commune.id
administrative_division_level_2_name: commune.district.name_en,
administrative_division_level_3_code: commune.id,
administrative_division_level_3_name: commune.name_en,
)

commune_ids = contact.metadata.fetch("commune_ids", [])
Expand Down
13 changes: 9 additions & 4 deletions app/models/call_flow_logic/mama_info_registration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -425,17 +425,22 @@ def persist_unconfirmed_date_of_birth
end

def persist_date_of_birth
contact = phone_call.contact
date_of_birth = metadata(phone_call, :unconfirmed_date_of_birth)
update_metadata!(phone_call, date_of_birth: date_of_birth)
update_metadata!(phone_call.contact, date_of_birth: date_of_birth)
phone_call.contact.metadata.delete("deregistered_at")
phone_call.contact.save!
update_metadata!(contact, date_of_birth: date_of_birth)
contact.metadata.delete("deregistered_at")
contact.date_of_birth = date_of_birth
contact.status = :active
contact.save!
end

def persist_deregistered
contact = phone_call.contact
update_metadata!(phone_call.contact, deregistered_at: Time.current)
phone_call.contact.metadata.delete("date_of_birth")
phone_call.contact.save!
contact.status = :disabled
contact.save!
end

def play(filename, response)
Expand Down
16 changes: 3 additions & 13 deletions app/models/contact.rb
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
class Contact < ApplicationRecord
extend Enumerize

COUNTRY_CODES = ISO3166::Country.codes.freeze

include MsisdnHelpers
include MetadataHelpers

enumerize :status, in: [ :active, :disabled ], scope: true
enumerize :gender, in: { male: "M", female: "F" }, scope: true
enumerize :iso_country_code, in: COUNTRY_CODES, scope: true
enumerize :status, in: [ :active, :disabled ], scope: :shallow
enumerize :gender, in: { male: "M", female: "F" }
enumerize :iso_country_code, in: ISO3166::Country.codes.freeze

belongs_to :account

Expand All @@ -30,15 +28,7 @@ class Contact < ApplicationRecord
to: :account,
allow_nil: true

before_create :assign_iso_country_code, unless: :iso_country_code?

def self.jsonapi_serializer_class
BeneficiarySerializer
end

private

def assign_iso_country_code
self.iso_country_code = PhonyRails.country_from_number(msisdn)
end
end
11 changes: 0 additions & 11 deletions app/request_schemas/v1/base_request_schema.rb

This file was deleted.

9 changes: 3 additions & 6 deletions app/request_schemas/v1/beneficiary_address_request_schema.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
module V1
class BeneficiaryAddressRequestSchema < BaseRequestSchema
class BeneficiaryAddressRequestSchema < JSONAPIRequestSchema
params do
required(:data).value(:hash).schema do
required(:type).filled(:str?, eql?: "address")
required(:attributes).value(:hash).schema do
required(:iso_region_code).maybe(:string)
required(:iso_country_code).filled(Types::UpcaseString, included_in?: Contact.iso_country_code.values)
required(:iso_region_code).filled(:string)
optional(:administrative_division_level_2_code).maybe(:string)
optional(:administrative_division_level_2_name).maybe(:string)
optional(:administrative_division_level_3_code).maybe(:string)
Expand All @@ -14,9 +15,5 @@ class BeneficiaryAddressRequestSchema < BaseRequestSchema
end
end
end

def output
super.except(:account)
end
end
end
5 changes: 3 additions & 2 deletions app/request_schemas/v1/beneficiary_request_schema.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
module V1
class BeneficiaryRequestSchema < BaseRequestSchema
class BeneficiaryRequestSchema < JSONAPIRequestSchema
params do
required(:data).value(:hash).schema do
required(:type).filled(:str?, eql?: "beneficiary")
Expand All @@ -12,7 +12,8 @@ class BeneficiaryRequestSchema < BaseRequestSchema
optional(:metadata).value(:hash)

optional(:address).filled(:hash).schema do
required(:iso_region_code).maybe(:string)
required(:iso_country_code).filled(Types::UpcaseString, included_in?: Contact.iso_country_code.values)
required(:iso_region_code).filled(:string)
optional(:administrative_division_level_2_code).maybe(:string)
optional(:administrative_division_level_2_name).maybe(:string)
optional(:administrative_division_level_3_code).maybe(:string)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
module V1
class UpdateBeneficiaryRequestSchema < BaseRequestSchema
class UpdateBeneficiaryRequestSchema < JSONAPIRequestSchema
params do
required(:data).value(:hash).schema do
required(:id).filled(:integer)
Expand Down
2 changes: 1 addition & 1 deletion app/serailizers/beneficiary_address_serializer.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
class BeneficiaryAddressSerializer < ResourceSerializer
set_type :address

attributes :iso_region_code,
attributes :iso_country_code, :iso_region_code,
:administrative_division_level_2_code,
:administrative_division_level_2_name,
:administrative_division_level_3_code,
Expand Down
21 changes: 20 additions & 1 deletion app/workflows/handle_phone_call_event.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,21 @@
class HandlePhoneCallEvent < ApplicationWorkflow
attr_accessor :url, :params

ACCOUNT_COUNTRY_CODES = {
2 => "SO",
3 => "KH",
4 => "KH",
7 => "SL",
8 => "TH",
11 => "US",
45 => "MX",
77 => "EG",
110 => "ZM",
143 => "KH",
209 => "LA"
}


def initialize(url, params = {})
self.url = url
self.params = params
Expand Down Expand Up @@ -60,6 +75,10 @@ def create_or_find_phone_call!(event)

def create_or_find_contact!(platform_account_sid, msisdn)
account = Account.find_by_platform_account_sid(platform_account_sid)
Contact.find_or_create_by!(account: account, msisdn: PhonyRails.normalize_number(msisdn))
Contact.find_or_create_by!(
account: account,
msisdn: PhonyRails.normalize_number(msisdn),
iso_country_code: ACCOUNT_COUNTRY_CODES.fetch(account.id, "KH")
)
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
class AddIsoCountryCodeToBeneficiaryAddresses < ActiveRecord::Migration[8.0]
def change
change_column_null :beneficiary_addresses, :iso_region_code, false
add_column :beneficiary_addresses, :iso_country_code, :citext, null: false

remove_index :beneficiary_addresses, [ :iso_region_code, :administrative_division_level_2_code, :administrative_division_level_3_code, :administrative_division_level_4_code ]
remove_index :beneficiary_addresses, [ :iso_region_code, :administrative_division_level_2_name, :administrative_division_level_3_name, :administrative_division_level_4_name ]

add_index :beneficiary_addresses, [ :beneficiary_id, :iso_country_code, :iso_region_code, :administrative_division_level_2_code, :administrative_division_level_3_code, :administrative_division_level_4_code ]
add_index :beneficiary_addresses, [ :beneficiary_id, :iso_country_code, :iso_region_code, :administrative_division_level_2_name, :administrative_division_level_3_name, :administrative_division_level_4_name ]
end
end
9 changes: 5 additions & 4 deletions db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema[8.0].define(version: 2024_12_11_092117) do
ActiveRecord::Schema[8.0].define(version: 2025_01_07_072933) do
# These are extensions that must be enabled in order to support this database
enable_extension "citext"
enable_extension "pg_catalog.plpgsql"
Expand Down Expand Up @@ -80,7 +80,7 @@

create_table "beneficiary_addresses", force: :cascade do |t|
t.bigint "beneficiary_id", null: false
t.citext "iso_region_code"
t.citext "iso_region_code", null: false
t.string "administrative_division_level_2_code"
t.string "administrative_division_level_2_name"
t.string "administrative_division_level_3_code"
Expand All @@ -89,9 +89,10 @@
t.string "administrative_division_level_4_name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.citext "iso_country_code", null: false
t.index ["beneficiary_id", "iso_country_code", "iso_region_code", "administrative_division_level_2_code", "administrative_division_level_3_code", "administrative_division_level_4_code"], name: "idx_on_beneficiary_id_iso_country_code_iso_region_c_069288d0e5"
t.index ["beneficiary_id", "iso_country_code", "iso_region_code", "administrative_division_level_2_name", "administrative_division_level_3_name", "administrative_division_level_4_name"], name: "idx_on_beneficiary_id_iso_country_code_iso_region_c_e888f7dc18"
t.index ["beneficiary_id"], name: "index_beneficiary_addresses_on_beneficiary_id"
t.index ["iso_region_code", "administrative_division_level_2_code", "administrative_division_level_3_code", "administrative_division_level_4_code"], name: "idx_on_iso_region_code_administrative_division_leve_a5183cd2b4"
t.index ["iso_region_code", "administrative_division_level_2_name", "administrative_division_level_3_name", "administrative_division_level_4_name"], name: "idx_on_iso_region_code_administrative_division_leve_c76774f7b0"
end

create_table "callout_participations", force: :cascade do |t|
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
module Subdivisions
Province = Struct.new(:code, :iso3166, :name_en, keyword_init: true)

ZAMBIA_PROVINCES = [
Province.new(code: "ZM-02", iso3166: "ZM-02", name_en: "Central"),
Province.new(code: "ZM-08", iso3166: "ZM-08", name_en: "Copperbelt"),
Province.new(code: "ZM-03", iso3166: "ZM-03", name_en: "Eastern"),
Province.new(code: "ZM-04", iso3166: "ZM-04", name_en: "Luapula"),
Province.new(code: "ZM-09", iso3166: "ZM-09", name_en: "Lusaka"),
Province.new(code: "ZM-10", iso3166: "ZM-10", name_en: "Muchinga"),
Province.new(code: "ZM-06", iso3166: "ZM-06", name_en: "North-Western"),
Province.new(code: "ZM-05", iso3166: "ZM-05", name_en: "Northern"),
Province.new(code: "ZM-07", iso3166: "ZM-07", name_en: "Southern"),
Province.new(code: "ZM-01", iso3166: "ZM-01", name_en: "Western")
]
end

ACCOUNT_COUNTRY_CODES = {
2 => "SO",
3 => "KH",
4 => "KH",
7 => "SL",
8 => "TH",
11 => "US",
45 => "MX",
77 => "EG",
110 => "ZM",
143 => "KH",
209 => "LA"
}

cache = {}

namespace :data_migrations do
task migrate_contact_metadata_to_native_fields: :environment do
Account.find_each do |account|
puts "Migrating contacts for account: #{account.id}"

contacts = account.contacts.where(iso_country_code: nil)
contacts.find_each do |contact|
ApplicationRecord.transaction do
contact.update_columns(
iso_country_code: ACCOUNT_COUNTRY_CODES.fetch(account.id),
language_code: contact.metadata["language_code"],
date_of_birth: contact.metadata["date_of_birth"],
status: contact.metadata["deregistered_at"].present? ? :disabled : :active
)

# EWS Cambodia
Array(contact.metadata["commune_ids"]).each do | commune_id |
cache[commune_id] ||= Pumi::Commune.find_by_id(commune_id)
commune = cache[commune_id]
next if commune.blank?

contact.addresses.find_or_create_by!(
iso_country_code: "KH",
iso_region_code: commune.province.iso3166_2,
administrative_division_level_2_code: commune.district_id,
administrative_division_level_2_name: commune.district.name_en,
administrative_division_level_3_code: commune.id,
administrative_division_level_3_name: commune.name_en,
created_at: contact.updated_at,
updated_at: contact.updated_at
)
end

# EWS Laos
Array(contact.metadata["registered_districts"]).each do | district_code |
district = CallFlowLogic::EWSLaosRegistration::DISTRICTS.find { |d| d.code == district_code }
next if district.blank?

contact.addresses.find_or_create_by!(
iso_country_code: "LA",
iso_region_code: district.province.iso3166,
administrative_division_level_2_code: district.code,
administrative_division_level_2_name: district.name_en,
created_at: contact.updated_at,
updated_at: contact.updated_at
)
end

# PIN Zambia
if account.id == 110
province = Subdivisions::ZAMBIA_PROVINCES.find { |d| d.name_en == contact.metadata["province"] }
next if province.blank?

contact.addresses.find_or_create_by!(
iso_country_code: "ZM",
iso_region_code: province.iso3166,
administrative_division_level_2_name: contact.metadata["district"],
administrative_division_level_3_name: contact.metadata["facility"]
)
end
end
end
end
end
end
3 changes: 2 additions & 1 deletion spec/factories.rb
Original file line number Diff line number Diff line change
Expand Up @@ -217,8 +217,9 @@
association :audio_file, factory: :active_storage_attachment, filename: "test.mp3"
end

factory :address, class: "BeneficiaryAddress" do
factory :beneficiary_address do
beneficiary
iso_country_code { "KH" }
iso_region_code { "KH-1" }
end
end
1 change: 1 addition & 0 deletions spec/models/call_flow_logic/ews_laos_registration_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ module CallFlowLogic
expect(contact.addresses.last).to have_attributes(
iso_region_code: "LA-CH",
administrative_division_level_2_code: "1604",
administrative_division_level_2_name: "Paksong",
)
assert_play("registration_successful-lao.mp3", response)
end
Expand Down
4 changes: 3 additions & 1 deletion spec/models/call_flow_logic/ews_registration_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,9 @@
expect(contact.addresses.last).to have_attributes(
iso_region_code: "KH-1",
administrative_division_level_2_code: "0105",
administrative_division_level_3_code: "010505"
administrative_division_level_2_name: "Ou Chrov",
administrative_division_level_3_code: "010505",
administrative_division_level_3_name: "Samraong"
)
assert_play("registration_successful-krr.wav", response)
end
Expand Down
Loading
Loading