DeveloperCare plansCare planLhr
Clinical Coding Support - Basic Implementation
Overview
The LHR system includes basic clinical coding support for India and USA to standardize medical terminology. This enables interoperability and compliance with healthcare standards in both countries.
Supported Code Systems:
- India: ICD-10 (mandatory for insurance claims)
- USA: ICD-10-CM, CPT (procedures), RxNorm (medications)
Database Schema
Clinical Codes Lookup Table (Simplified)
# db/migrate/[timestamp]_create_clinical_codes.rb
class CreateClinicalCodes < ActiveRecord::Migration[7.0]
def change
create_table :clinical_codes do |t|
# Core fields
t.string :code_system, null: false # 'ICD10', 'ICD10CM', 'CPT', 'RxNorm'
t.string :code, null: false # The actual code (e.g., 'E11.9', '99213')
t.string :display_name, null: false # Human readable name
t.text :description # Detailed description
# Country and category
t.string :country # 'IN' for India, 'US' for USA, null for both
t.string :category # 'diagnosis', 'procedure', 'medication'
t.string :status, default: 'active' # active, deprecated
# Search optimization
t.text :searchable_text # Concatenated searchable content
# Usage tracking
t.integer :usage_count, default: 0
t.datetime :last_used_at
t.timestamps
end
# Indexes for performance
add_index :clinical_codes, [:code_system, :code], unique: true
add_index :clinical_codes, :display_name
add_index :clinical_codes, [:country, :category]
add_index :clinical_codes, :searchable_text, using: :gin
add_index :clinical_codes, :usage_count
end
endModel Implementation
ClinicalCode Model (Simplified)
# app/models/clinical_code.rb
class ClinicalCode < ApplicationRecord
# Validations
validates :code_system, :code, :display_name, presence: true
validates :code, uniqueness: { scope: :code_system }
# Callbacks
before_save :generate_searchable_text
# Scopes
scope :active, -> { where(status: 'active') }
scope :for_country, ->(country) { where(country: [country, nil]) }
scope :for_category, ->(category) { where(category: category) }
scope :diagnosis_codes, -> { where(category: 'diagnosis') }
scope :procedure_codes, -> { where(category: 'procedure') }
scope :medication_codes, -> { where(category: 'medication') }
# Search methods
def self.search(query, options = {})
scope = active
# Filter by country if clinic country is provided
if options[:country].present?
scope = scope.for_country(options[:country])
end
# Filter by category
if options[:category].present?
scope = scope.for_category(options[:category])
end
# Filter by code system
if options[:code_system].present?
scope = scope.where(code_system: options[:code_system])
end
# Search in code and display name
if query.present?
search_term = "%#{query.downcase}%"
scope = scope.where(
"LOWER(code) LIKE ? OR LOWER(display_name) LIKE ? OR LOWER(searchable_text) LIKE ?",
search_term, search_term, search_term
)
# Order by relevance: exact code match, exact name match, then others
scope = scope.order(
Arel.sql("CASE
WHEN LOWER(code) = '#{query.downcase}' THEN 1
WHEN LOWER(display_name) = '#{query.downcase}' THEN 2
ELSE 3 END, usage_count DESC")
)
end
scope.limit(options[:limit] || 10)
end
# Instance methods
def full_display
"#{code} - #{display_name}"
end
def record_usage!
increment!(:usage_count)
touch(:last_used_at)
end
private
def generate_searchable_text
self.searchable_text = [code, display_name, description].compact.join(' ').downcase
end
endIntegration with Encounters
Add Clinical Codes to Complaints
# Add to encounter_complaints table
add_column :encounter_complaints, :icd10_code, :string
add_column :encounter_complaints, :icd11_code, :string # Future supportAdd Medication Codes
# Add to encounter_medicines table
add_column :encounter_medicines, :ndc_code, :string # National Drug Code (USA)
add_column :encounter_medicines, :generic_name, :stringService Layer
Clinical Coding Service (Simplified)
# app/services/clinical_coding_service.rb
class ClinicalCodingService
attr_reader :country
def initialize(clinic_country = 'IN')
@country = clinic_country # 'IN' for India, 'US' for USA
end
# Search for diagnosis codes based on text
def find_diagnosis_codes(text, limit: 5)
return [] if text.blank?
# Determine which code system to use based on country
code_system = @country == 'US' ? 'ICD10CM' : 'ICD10'
ClinicalCode.search(text,
country: @country,
category: 'diagnosis',
code_system: code_system,
limit: limit
)
end
# Search for procedure codes (USA only)
def find_procedure_codes(text, limit: 5)
return [] unless @country == 'US' && text.present?
ClinicalCode.search(text,
country: 'US',
category: 'procedure',
code_system: 'CPT',
limit: limit
)
end
# Search for medication codes
def find_medication_codes(text, limit: 5)
return [] if text.blank?
ClinicalCode.search(text,
country: @country,
category: 'medication',
code_system: 'RxNorm',
limit: limit
)
end
# Get commonly used diagnosis codes
def common_diagnosis_codes
ClinicalCode
.diagnosis_codes
.for_country(@country)
.where('usage_count > ?', 10)
.order(usage_count: :desc)
.limit(20)
end
# Get commonly used codes by specialty
def codes_by_specialty(specialty)
# Map specialties to common conditions
specialty_conditions = {
'cardiology' => ['hypertension', 'heart', 'cardiac', 'coronary'],
'diabetology' => ['diabetes', 'glucose', 'insulin'],
'general' => ['fever', 'cough', 'cold', 'headache']
}
search_terms = specialty_conditions[specialty.downcase] || []
results = []
search_terms.each do |term|
results += find_diagnosis_codes(term, limit: 3)
end
results.uniq.take(10)
end
endAPI Endpoints
Clinical Codes Controller
# app/controllers/api/clinical_codes_controller.rb
class Api::ClinicalCodesController < ApiController
before_action :authenticate_user!
before_action :set_clinic_country
# GET /api/clinical_codes/search?q=diabetes&category=diagnosis
def search
service = ClinicalCodingService.new(@clinic_country)
codes = case params[:category]
when 'diagnosis'
service.find_diagnosis_codes(params[:q])
when 'procedure'
service.find_procedure_codes(params[:q])
when 'medication'
service.find_medication_codes(params[:q])
else
ClinicalCode.search(params[:q], country: @clinic_country)
end
render json: {
query: params[:q],
country: @clinic_country,
results: codes.map { |code| serialize_code(code) }
}
end
# GET /api/clinical_codes/common
def common
service = ClinicalCodingService.new(@clinic_country)
codes = if params[:specialty].present?
service.codes_by_specialty(params[:specialty])
else
service.common_diagnosis_codes
end
render json: {
country: @clinic_country,
codes: codes.map { |code| serialize_code(code) }
}
end
private
def set_clinic_country
# Get country from user's clinic or default to India
@clinic_country = current_user.clinic&.country || 'IN'
end
def serialize_code(code)
{
id: code.id,
code_system: code.code_system,
code: code.code,
display_name: code.display_name,
category: code.category,
full_display: code.full_display
}
end
endSeed Data Examples
# db/seeds/clinical_codes.rb
# Common ICD-10 codes for India
indian_codes = [
{ code: 'J06.9', display_name: 'Acute upper respiratory infection', description: 'Common cold' },
{ code: 'I10', display_name: 'Essential hypertension', description: 'High blood pressure' },
{ code: 'E11.9', display_name: 'Type 2 diabetes mellitus', description: 'Diabetes without complications' },
{ code: 'J44.0', display_name: 'COPD with acute lower respiratory infection', description: 'Chronic obstructive pulmonary disease' },
{ code: 'N39.0', display_name: 'Urinary tract infection', description: 'UTI' }
]
indian_codes.each do |code_data|
ClinicalCode.create!(
code_system: 'ICD10',
code: code_data[:code],
display_name: code_data[:display_name],
description: code_data[:description],
country: 'IN',
category: 'diagnosis',
status: 'active'
)
end
# Common ICD-10-CM codes for USA
usa_diagnosis_codes = [
{ code: 'I10', display_name: 'Essential (primary) hypertension', description: 'High blood pressure' },
{ code: 'E11.9', display_name: 'Type 2 diabetes mellitus without complications', description: 'Diabetes' },
{ code: 'J06.9', display_name: 'Acute upper respiratory infection, unspecified', description: 'URI' }
]
usa_diagnosis_codes.each do |code_data|
ClinicalCode.create!(
code_system: 'ICD10CM',
code: code_data[:code],
display_name: code_data[:display_name],
description: code_data[:description],
country: 'US',
category: 'diagnosis',
status: 'active'
)
end
# Common CPT procedure codes for USA
usa_procedure_codes = [
{ code: '99213', display_name: 'Office visit, established patient, 15 minutes', description: 'Level 3 office visit' },
{ code: '99214', display_name: 'Office visit, established patient, 25 minutes', description: 'Level 4 office visit' },
{ code: '90834', display_name: 'Psychotherapy, 45 minutes', description: 'Individual psychotherapy' }
]
usa_procedure_codes.each do |code_data|
ClinicalCode.create!(
code_system: 'CPT',
code: code_data[:code],
display_name: code_data[:display_name],
description: code_data[:description],
country: 'US',
category: 'procedure',
status: 'active'
)
end
# Common RxNorm medication codes (used in both countries)
medication_codes = [
{ code: '29046', display_name: 'Lisinopril', description: 'ACE inhibitor for hypertension' },
{ code: '6809', display_name: 'Metformin', description: 'Diabetes medication' },
{ code: '1719', display_name: 'Azithromycin', description: 'Antibiotic' }
]
medication_codes.each do |code_data|
ClinicalCode.create!(
code_system: 'RxNorm',
code: code_data[:code],
display_name: code_data[:display_name],
description: code_data[:description],
country: nil, # Available in both countries
category: 'medication',
status: 'active'
)
endUsage in UI
// Frontend component for code selection
const DiagnosisCodeSelector = ({ country, onSelect }) => {
const [searchTerm, setSearchTerm] = useState('');
const [codes, setCodes] = useState([]);
const searchCodes = async (query) => {
const response = await api.get('/api/clinical_codes/search', {
params: { q: query, category: 'diagnosis' }
});
setCodes(response.data.results);
};
return (
<div>
<input
type="text"
placeholder={country === 'US' ? "Search ICD-10-CM codes..." : "Search ICD-10 codes..."}
value={searchTerm}
onChange={(e) => {
setSearchTerm(e.target.value);
if (e.target.value.length > 2) {
searchCodes(e.target.value);
}
}}
/>
<ul>
{codes.map(code => (
<li key={code.id} onClick={() => onSelect(code)}>
{code.full_display}
</li>
))}
</ul>
</div>
);
};Benefits of This Simplified Approach:
✅ Country-Specific: Supports both Indian (ICD-10) and USA (ICD-10-CM, CPT) requirements
✅ Simple Implementation: No complex hierarchies or relationships
✅ Fast Search: Optimized for quick code lookup
✅ Usage Tracking: Learns from user behavior
✅ Easy to Maintain: Straightforward data model
Next: See api-design.md for API implementation details.