Skip to content

Commit

Permalink
Consolidating the use of dynamic programming to use the new PrimeroMo…
Browse files Browse the repository at this point in the history
…delService.
  • Loading branch information
pnabutovsky committed May 24, 2024
1 parent ddf901b commit 4da9b89
Show file tree
Hide file tree
Showing 27 changed files with 118 additions and 116 deletions.
2 changes: 1 addition & 1 deletion app/controllers/api/v2/bulk_exports_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def model_class
def authorize_export!
export_format = export_params[:export_format] == 'xlsx' ? 'xls' : export_params[:export_format]
action = "export_#{export_format}".to_sym
record_model = export_params[:record_type] && Record.model_from_name(export_params[:record_type])
record_model = export_params[:record_type] && PrimeroModelService.to_model(export_params[:record_type])
authorize! action, record_model
end

Expand Down
2 changes: 1 addition & 1 deletion app/controllers/api/v2/users_transitions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def authorize_assign!(record)
end

def record_model
@record_model = Record.model_from_name(params[:record_type])
@record_model = PrimeroModelService.to_model(params[:record_type])
end

def record_module_unique_id
Expand Down
3 changes: 2 additions & 1 deletion app/controllers/application_api_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ class << self
attr_accessor :model_class
end

# NOTE: If request unit tests are breaking, make sure to update the list of permitted models in PrimeroModelService
def model_class
@model_class ||= Record.model_from_name(request.path.split('/')[3].singularize)
@model_class ||= PrimeroModelService.to_model(request.path.split('/')[3].singularize)
end

def record_id
Expand Down
2 changes: 1 addition & 1 deletion app/jobs/duplicate_field_alert_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ class DuplicateFieldAlertJob < ApplicationJob
queue_as :api

def perform(record_id, record_type, alert_on_duplicate)
record = Record.model_from_name(record_type)&.find_by(id: record_id)
record = PrimeroModelService.to_model(record_type)&.find_by(id: record_id)
return unless record.present?

DuplicatedFieldAlertService.create_or_remove_alerts!(record, alert_on_duplicate)
Expand Down
2 changes: 1 addition & 1 deletion app/jobs/webhook_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ class WebhookJob < ApplicationJob
queue_as :api

def perform(record_type, record_id, action)
record = Record.model_from_name(record_type).find_by(id: record_id)
record = PrimeroModelService.to_model(record_type)&.find_by(id: record_id)
return unless record&.webhook_configured?

webhooks = Webhook.webhooks_for(record, action)
Expand Down
2 changes: 1 addition & 1 deletion app/models/bulk_export.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def export(password)
end

def model_class
@model_class ||= Record.model_from_name(record_type)
@model_class ||= PrimeroModelService.to_model(record_type)
end

def exporter_type
Expand Down
2 changes: 1 addition & 1 deletion app/models/concerns/duplicate_id_alertable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ module DuplicateIdAlertable
def perform_duplicate_field_alert
return unless alerts_on_duplicate.present?

record_alerts_on_duplicate = alerts_on_duplicate[Record.map_name(self.class.name)]
record_alerts_on_duplicate = alerts_on_duplicate[PrimeroModelService.to_name(self.class.name)]

return unless record_alerts_on_duplicate.present?

Expand Down
15 changes: 0 additions & 15 deletions app/models/concerns/record.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,21 +21,6 @@ module Record
before_save :populate_subform_ids
end

def self.model_from_name(name)
case name
when 'case' then Child
when 'violation' then Incident
else Object.const_get(name.camelize)
end
rescue NameError
nil
end

def self.map_name(name)
name = name.underscore
name == 'child' ? 'case' : name
end

# Class methods for all Record types
module ClassMethods
def new_with_user(user, data = {})
Expand Down
11 changes: 0 additions & 11 deletions app/models/export_configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,10 @@ class ExportConfiguration < ApplicationRecord
localize_properties :name

validate :valid_record_type
validate :opt_out_field_exists

def valid_record_type
return true if %w[Child TracingRequest Incident].include?(record_type)

errors.add(:record_type, I18n.t('errors.models.export_configuration.record_type'))
end

# If there is a consent field defined, make sure it is a valid property of the Record
def opt_out_field_exists
return true if opt_out_field.blank?

klass = Object.const_get(record_type)
return true if klass.method_defined?(opt_out_field)

errors.add(:opt_out_field, I18n.t('errors.models.export_configuration.opt_out_field_does_not_exist'))
end
end
2 changes: 1 addition & 1 deletion app/models/exporters/configurable_exporter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ def opt_out_property_keys
def opting_out?(record)
return false if @export_configuration.blank? || @export_configuration.opt_out_field.blank?

record.try(:send, @export_configuration.opt_out_field) == true
record&.data&.[](@export_configuration.opt_out_field) == true
end

def write_header(rows)
Expand Down
6 changes: 3 additions & 3 deletions app/models/exporters/ruby_config_exporter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ def file_for(config_name, config_objects = nil)

def export
# TODO: Location, PrimeroModule, PrimeroProgram, SystemSettings, ExportConfiguration, IdentityProvider
%w[Agency Lookup Report UserGroup Role ContactInformation].each do |config_name|
config_objects = Object.const_get(config_name).all.map(&:configuration_hash)
export_config_objects(config_name, config_objects)
[Agency, Lookup, Report, UserGroup, Role, ContactInformation].each do |config|
config_objects = config.all.map(&:configuration_hash)
export_config_objects(config.name, config_objects)
end
export_forms
end
Expand Down
13 changes: 12 additions & 1 deletion app/models/kpi/search.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,24 @@

module Kpi
SearchValue = Struct.new(:from, :to, :owned_by_groups, :owned_by_agency_id)
VALID_KPIS = %w[
KpiAssessmentStatus KpiAverageFollowupMeetingsPerCase KpiAverageReferrals KpiCaseClosureRate KpiCaseLoad
KpiClientSatisfactionRate KpiCompletedCaseActionPlans KpiCompletedCaseSafetyPlans
KpiCompletedSupervisorApprovedCaseActionPlans KpiNumberOfCases KpiNumberOfIncidents KpiReportingDelay
KpiServicesProvided KpiSupervisorToCaseworkerRatio KpiTimeFromCaseOpenToClose
].freeze

# Search
#
# An abstract class for search objects to subclass.
class Search < SearchValue
def self.find(id)
Object.const_get("Kpi::#{id.camelize}")
name = "Kpi::#{id.classify}"
return unless VALID_KPIS.include?(name)

Object.const_get(name)
rescue NameError
nil
end

def self.search_model(model = nil)
Expand Down
14 changes: 6 additions & 8 deletions app/models/primero_configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
# This model persists the user-modifiable state of the Primero configuration as JSON.
# If desired, this configuration state can replace the current Primero configuration state.
class PrimeroConfiguration < ApplicationRecord
CONFIGURABLE_MODELS = %w[FormSection Lookup Agency Role UserGroup Report ContactInformation].freeze
CONFIGURABLE_MODELS = [FormSection, Lookup, Agency, Role, UserGroup, Report, ContactInformation].freeze

PRIMERO_CONFIGURATION_FIELDS_SCHEMA = {
'id' => { 'type' => 'string', 'format' => 'regex', 'pattern' => PermittedFieldService::UUID_REGEX },
Expand Down Expand Up @@ -49,8 +49,7 @@ def current(created_by = nil)

def current_configuration_data
CONFIGURABLE_MODELS.each_with_object({}) do |model, data|
model_class = Kernel.const_get(model)
data[model] = model_class.all.map(&:configuration_hash)
data[model.name] = model.all.map(&:configuration_hash)
end
end

Expand Down Expand Up @@ -97,11 +96,10 @@ def can_apply?

def configure!
CONFIGURABLE_MODELS.each do |model|
next unless data.key?(model)
next unless data.key?(model.name)

model_class = Kernel.const_get(model)
model_class.sort_configuration_hash(data[model]).each do |configuration|
model_class.create_or_update!(configuration)
model.sort_configuration_hash(data[model.name]).each do |configuration|
model.create_or_update!(configuration)
end
end
end
Expand All @@ -120,7 +118,7 @@ def remainder(model_class)

def validate_configuration_data
data_is_valid = CONFIGURABLE_MODELS.reduce(true) do |valid, model|
valid && (%w[Report Location].include?(model) || data[model]&.size&.positive?)
valid && ([Report, Location].include?(model) || data[model.name]&.size&.positive?)
end
return if data_is_valid

Expand Down
54 changes: 11 additions & 43 deletions app/models/report.rb
Original file line number Diff line number Diff line change
Expand Up @@ -55,49 +55,21 @@ class Report < ApplicationRecord
before_create :generate_unique_id
before_save :apply_default_filters

def self.new_with_properties(report_params)
report = Report.new(report_params.except(:name, :description, :fields))
report.name_i18n = report_params[:name]
report.description_i18n = report_params[:description]
report.aggregate_by = ReportFieldService.aggregate_by_from_params(report_params)
report.disaggregate_by = ReportFieldService.disaggregate_by_from_params(report_params)
report
end

def validate_name_in_base_language
return if name_en.present?

errors.add(:name, I18n.t('errors.models.report.name_presence'))
end

class << self
def get_reportable_subform_record_field_name(model, record_type)
model = Record.model_from_name(model)
return unless model.try(:nested_reportable_types)

model.nested_reportable_types.select { |nrt| nrt.model_name.param_key == record_type }.first&.record_field_name
end

def get_reportable_subform_record_field_names(model)
model = Record.model_from_name(model)
return unless model.try(:nested_reportable_types)

model.nested_reportable_types.map { |nrt| nrt.model_name.param_key }
end

def record_type_is_nested_reportable_subform?(model, record_type)
get_reportable_subform_record_field_names(model).include?(record_type)
end

def all_nested_reportable_types
record_types = []
FormSection::RECORD_TYPES.each do |rt|
record_types += Record.model_from_name(rt).try(:nested_reportable_types)
end
record_types
end

def new_with_properties(report_params)
report = Report.new(report_params.except(:name, :description, :fields))
report.name_i18n = report_params[:name]
report.description_i18n = report_params[:description]
report.aggregate_by = ReportFieldService.aggregate_by_from_params(report_params)
report.disaggregate_by = ReportFieldService.disaggregate_by_from_params(report_params)
report
end
end

def update_properties(report_params)
report_params = report_params.with_indifferent_access if report_params.is_a?(Hash)
converted_params = FieldI18nService.convert_i18n_properties(Report, report_params)
Expand Down Expand Up @@ -158,7 +130,7 @@ def lookups
end

def model
@model ||= Record.model_from_name(record_type)
@model ||= PrimeroModelService.to_model(record_type)
end

def build_query
Expand Down Expand Up @@ -289,15 +261,11 @@ def validate_modules_present
end
end

def self.reportable_record_types
FormSection::RECORD_TYPES + ['violation'] + Report.all_nested_reportable_types.map { |nrt| nrt.name.underscore }
end

def apply_default_filters
return unless add_default_filters

self.filters ||= []
default_filters = Record.model_from_name(record_type).report_filters
default_filters = model.report_filters
self.filters = (self.filters + default_filters).uniq
end

Expand Down
6 changes: 3 additions & 3 deletions app/services/data_removal_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,10 @@ def to_query(query, filter)
)
end

def model_classes(models)
return [] unless models.present?
def model_classes(model_names)
return [] unless model_names.present?

models.map { |model| Kernel.const_get(model) }
model_names.map { |name| PrimeroModelService.to_model(name) }
end

def remove_from_solr
Expand Down
2 changes: 1 addition & 1 deletion app/services/field_value_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ def record_name_value(class_name, value, opts = {})
record_class = if class_name.in?(%w[Location ReportingLocation])
location_service
else
Object.const_get(class_name)
Agency
end

return value unless record_class
Expand Down
2 changes: 1 addition & 1 deletion app/services/permitted_attachment_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ def previewable_type?
def permitted_field_names
@permitted_field_names ||= permitted_form_fields_service.permitted_field_names(
authorized_roles,
Record.map_name(attachment.record_type),
PrimeroModelService.to_name(attachment.record_type),
write
)
end
Expand Down
33 changes: 33 additions & 0 deletions app/services/primero_model_service.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# frozen_string_literal: true

# Copyright (c) 2014 - 2024 UNICEF. All rights reserved.

# Use this service to convert name strings of Primero models into class objects.
# Thsi is useful if a record saves a type field. Only permitted strings can be turned into classes.
class PrimeroModelService
VALID_MODEL_NAMES = %w[
Child Incident Violation TracingRequest Trace RegistryRecord Family
ReportableFollowUp ReportableProtectionConcern ReportableService Dashboard
Flag Alert Attachment AuditLog BulkExport RecordHistory SavedSearch Transition Task ActivityLog
Agency ContactInformation Field FormSection Location Lookup PrimeroModule PrimeroProgram Report User Role
Permission SystemSettings UserGroup ExportConfiguration PrimeroConfiguration Webhook IdentityProvider
].freeze

def self.to_model(name)
name = if %w[case Case].include?(name)
'Child'
else
name.classify
end
return unless VALID_MODEL_NAMES.include?(name)

Object.const_get(name)
rescue NameError
nil
end

def self.to_name(name)
name = name.underscore
name == 'child' ? 'case' : name
end
end
2 changes: 1 addition & 1 deletion app/views/api/v2/flags/_flag.json.jbuilder
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ json.name RecordDataService.visible_name(flag&.record)
json.short_id flag&.record&.short_id
json.owned_by flag&.record&.owned_by
json.owned_by_agency_id flag&.record&.owned_by_agency_id
json.record_type Record.map_name(flag.record_type).pluralize
json.record_type PrimeroModelService.to_name(flag.record_type).pluralize

record_access_denied = !current_user.can?(:read, flag&.record)
json.record_access_denied record_access_denied
Expand Down
2 changes: 1 addition & 1 deletion app/views/api/v2/potential_matches/index.json.jbuilder
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ json.data do
end
json.record do
json.id @record.id
json.type Record.map_name(@record.class.name)
json.type PrimeroModelService.to_name(@record.class.name)
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ if primero_module.unique_id == PrimeroModule::CP
json.workflows do
if primero_module.workflow_status_indicator
['case'].each do |record_type|
record_class = Record.model_from_name(record_type)
record_class = PrimeroModelService.to_model(record_type)
json.set! record_type do
json.merge! FieldI18nService.convert_options(record_class.workflow_statuses([primero_module]))
end
Expand Down
2 changes: 1 addition & 1 deletion app/views/api/v2/record_histories/index.json.jbuilder
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
json.data do
json.array! @record_histories do |record_history|
json.record_id @record.id
json.record_type Record.map_name(record_history.record_type).pluralize
json.record_type PrimeroModelService.to_name(record_history.record_type).pluralize
json.datetime record_history.datetime&.iso8601
json.user_name record_history.user_name
json.action record_history.action
Expand Down
2 changes: 1 addition & 1 deletion app/views/api/v2/transitions/_transition.json.jbuilder
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
json.merge!(
transition.attributes.to_h do |attr, value|
if attr == 'record_type'
[attr, Record.map_name(value)]
[attr, PrimeroModelService.to_name(value)]
else
[attr, value]
end
Expand Down
Loading

0 comments on commit 4da9b89

Please sign in to comment.