desc "Application specific tasks" namespace :saucy do desc "Background sync operations" task sync: :environment do Member.reset_all_status! end desc "Send daily email notifications" task notify: :environment do Notification.send_scheduled_for_today end desc "Import data from CiviCRM XML files" task import: :environment do require 'nokogiri' require 'date' Notification.delete_all Contribution.delete_all Member.delete_all def sql_resultset_to_hashes(resultset_filename) Nokogiri::XML(File.read(resultset_filename)) .xpath('resultset/row') .map do |row| row .xpath('field') .map { |field| [field["name"], field.children.size > 0 ? field.text : nil] } .to_h end end def ensure_one(list) raise if list.size > 1 list[0] end dir = ENV.fetch('CIVICRM_DUMP_DIRECTORY') addresses = sql_resultset_to_hashes("#{dir}/address.xml").select { |a| a["is_primary"] == "1" } contacts = sql_resultset_to_hashes("#{dir}/contact.xml") contributions = sql_resultset_to_hashes("#{dir}/contribution.xml") emails = sql_resultset_to_hashes("#{dir}/email.xml").select { |a| a["is_primary"] == "1" } memberships = sql_resultset_to_hashes("#{dir}/membership.xml").select { |m| Date.parse(m['end_date']).year >= 2020 } payment_instruments = sql_resultset_to_hashes("#{dir}/payment_instrument.xml") statuses = sql_resultset_to_hashes("#{dir}/status.xml") types = sql_resultset_to_hashes("#{dir}/type.xml") members = contacts.map do |contact| contact.merge( "membership" => memberships.select { |m| m['contact_id'] == contact['id'] }.then { |ms| ensure_one(ms) }, "address" => addresses.select { |a| a['contact_id'] == contact['id'] }.then { |ms| ensure_one(ms) }, "email" => emails.select { |a| a['contact_id'] == contact['id'] }.then { |ms| ensure_one(ms) }, "contributions" => contributions.select { |c| c['contact_id'] == contact['id'] }, ) end members = members.select { |m| !m['membership'].nil? } raise "we have duplicates" unless members.map { |m| m['display_name'] }.count == members.map { |m| m['display_name'] }.uniq.count members.each do |member| address = member['address'] address = if address postal_code = [ address['postal_code'], address['postal_code_suffix'] ].reject(&:nil?).reject(&:empty?).join('-') [ address['street_address'], address['supplemental_address_1'], address['supplemental_address_2'], address['supplemental_address_3'], "#{postal_code} #{address['city']}", ].reject(&:nil?).reject(&:empty?).join("\n") else nil end category = case member['membership']['membership_type_id'] when "1" then 'student' when "2" then 'employed' when "3" then 'retired' when "4" then 'unemployed' else raise "derp membership type" end m = Member.new( number: member['membership']['id'], email: member['email']['email'], display_name: member['display_name'], identification_number: member['legal_identifier'], status: "", # must be reset category: category, # membership type id address: address, joined_on: member['membership']['join_date'], expires_on: member['membership']['end_date'], #t.string "regular_ifthenpay_link" #t.string "reduced_ifthenpay_link" excluded: false, wants_mailing_list: true, prefers_postal: false, ) m.save! m.reset_status! member['contributions'].each do |contribution| c = Contribution.new( member: m, eurocents: contribution['total_amount'], payment_on: contribution['receive_date'] || m.joined_on, payment_method: payment_instruments.find { |i| i["value"] == contribution['payment_instrument_id'] }["name"], payment_reference: "", ) c.save! end end end end