houdini/lib/insert/insert_custom_field_joins.rb
Bradley M. Kuhn 6772312ea7 Relicense all .rb files under new project license.
The primary license of the project is changing to:
  AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later

with some specific files to be licensed under the one of two licenses:
   CC0-1.0
   LGPL-3.0-or-later

This commit is one of the many steps to relicense the entire codebase.

Documentation granting permission for this relicensing (from all past
contributors who hold copyrights) is on file with Software Freedom
Conservancy, Inc.
2018-03-25 15:10:40 -04:00

143 lines
5.9 KiB
Ruby

# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later
require 'delayed_job_helper'
require 'qx'
require 'update/update_custom_field_joins'
module InsertCustomFieldJoins
# Bulk insert many field joins into many supporters
# for every field name, find or create it for the nonprofit
# field_data should be an array of arrays liks [['Company', 'Pixar'],
# ['Shirt-size', 'Small']]
def self.find_or_create(np_id, supporter_ids, field_data)
ParamValidation.new({np_id: np_id, supporter_ids: supporter_ids, field_data: field_data},
{
:np_id => {
:required => true,
:is_integer=> true
},
:supporter_ids => {
:required => true,
:is_array => true,
:min_length => 1
},
:field_data => {
:required => true,
:is_array => true,
:min_length => 1
}
})
#make sure the np exists
np = Nonprofit.where("id = ? ", np_id).first
unless np
raise ParamValidation::ValidationError.new("#{np_id} is not a valid non-profit", {:key => :np_id})
end
#make sure the supporters_ids exist
supporter_ids.each {|id|
unless np.supporters.where('id = ?', id).exists?
raise ParamValidation::ValidationError.new("#{id} is not a valid supporter for nonprofit #{np_id}", {:key => :supporter_ids})
end
}
Qx.transaction do
# get the custom_field_master_id for each field_data name
cfm_id_to_value = field_data.map { |name, value|
cfm = CustomFieldMaster.where("nonprofit_id = ? and name = ?", np_id, name).first
unless cfm
cfm = CustomFieldMaster.create!(:nonprofit => np, :name => name)
end
{:custom_field_master_id => cfm.id, :value => value}
}
return in_bulk(np_id, supporter_ids, cfm_id_to_value)
end
end
# Validation: *np_id is valid, corresponds to real nonprofit
#
#
# @param [Integer] np_id nonprofit_id whose custom_fields this applies to
# @param [Array<Integer>] supporter_ids the supporter ids in which the custom fields should be modified
# @param [Array<Hash<Symbol, Object>>] field_data the fields you'd like to modify. Each item is a hash with following keys:
# * custom_field_master_id [Integer] for the key corresponding to custom_field_master_id
# * value [Object] the expected value of the field. If this key is an empty string, we remove the custom_field
def self.in_bulk(np_id, supporter_ids, field_data)
begin
ParamValidation.new({
np_id: np_id,
supporter_ids: supporter_ids,
field_data: field_data
}, {
np_id: {required: true, is_integer: true},
supporter_ids: {required:true, is_array: true},
field_data: { required: true, is_array: true}
# array_of_hashes: {
# selected: {required: true}, tag_master_id: {required: true, is_integer: true}
# }
})
rescue ParamValidation::ValidationError => e
return {json: {error: "Validation error\n #{e.message}", errors: e.data}, status: :unprocessable_entity}
end
begin
return {json: {error: "Nonprofit #{np_id} is not valid"}, status: :unprocessable_entity} unless Nonprofit.exists?(np_id)
# verify that the supporters belong to the nonprofit
supporter_ids = Supporter.where('nonprofit_id = ? and id IN (?)', np_id, supporter_ids).pluck(:id)
unless supporter_ids.any?
return {json: {inserted_count: 0, removed_count: 0}, status: :ok}
end
# filtering the tag_data to this nonprofit
valid_ids = CustomFieldMaster.where('nonprofit_id = ? and id IN (?)', np_id, field_data.map {|fd| fd[:custom_field_master_id] }).pluck(:id).to_a
filtered_field_data = field_data.select {|i| valid_ids.include? i[:custom_field_master_id ].to_i}
# first, delete the items which should be removed
to_insert, to_remove = filtered_field_data.partition{|t|
!t[:value].blank?
}
deleted = []
if to_remove.any?
deleted = Qx.delete_from(:custom_field_joins)
.where("supporter_id IN ($ids)", ids: supporter_ids)
.and_where("custom_field_master_id in ($fields)", fields: to_remove.map{|t| t[:custom_field_master_id]})
.returning('*')
.execute
end
# next add only the selected tag_joins
if to_insert.any?
insert_data = supporter_ids.map{|id| to_insert.map{|cfm| {supporter_id: id, custom_field_master_id: cfm[:custom_field_master_id], value: cfm[:value]}}}.flatten
cfj = Qx.insert_into(:custom_field_joins)
.values(insert_data)
.timestamps
.on_conflict()
.conflict_columns(:supporter_id, :custom_field_master_id).upsert(:custom_field_join_supporter_unique_idx)
.returning('*')
.execute
else
cfj = []
end
rescue ActiveRecord::ActiveRecordError => e
return {json: {error: "A DB error occurred. Please contact support. \n #{e.message}"}, status: :unprocessable_entity}
end
# Create an activity for the modified tags for every supporter
# TODO
# activity_data = tags.map{|tag| {supporter_id: tag['supporter_id'], nonprofit_id: np_id, attachment_id: tag['id'], attachment_type: 'TagJoin', profile_id: profile_id}}
# activities = Psql.execute( Qexpr.new.insert(:activities, activity_data) )
# Sync mailchimp lists, if present
#Mailchimp.delay.sync_supporters_to_list_from_tag_joins(np_id, supporter_ids, tag_data)
return {json: {inserted_count: cfj.count, removed_count: deleted.count }, status: :ok}
end
end