module Eco module API module Organization class People < Eco::Language::Models::Collection # build the shortcuts of Collection attr_collection :id, :external_id, :email, :name, :supervisor_id attr_presence :account, :details alias_method :people, :to_a def initialize(people = [], klass: Ecoportal::API::Internal::Person, factory: nil) @klass = Ecoportal::API::Internal::Person unless klass == Ecoportal::API::V1::Person super(people, klass: @klass) @caches_init = false end def [](id_or_ext) id(id_or_ext) || external_id(id_or_ext) end def id(*args) attr('id', *args).first end def external_id(*args) attr('external_id', *args).first end def users account_present(true) end def contacts details_present(true) end def non_users account_present(false) end # It searches a person using the paramters given. # @param id [String] the `internal id` of the person # @param external_id [String] the `exernal_id` of the person # @param email [String] the `email` of the person # @param strict [Boolean] if should perform a `soft` or a `strict` search. `strict` will avoid repeated email addresses. # @return [Person, nil] the person we were searching, or `nil` if not found. def person(id: nil, external_id: nil, email: nil, strict: false) init_caches pers = @by_id[id]&.first if id pers = @by_external_id[external_id&.strip]&.first if !pers && !external_id.to_s.strip.empty? # strict prevents taking existing user for searched person with same email # specially useful if the organisation ensures all have external id (no need for email search) if !pers && (!strict || external_id.to_s.strip.empty?) # person still not found and either not strict or no external_id provided pers = @by_users_email[email&.downcase.strip]&.first if !email.to_s.strip.empty? if !pers && !strict && !email.to_s.strip.empty? candidates = @by_non_users_email[email&.downcase.strip] || [] raise "Too many non-user candidates (#{candiates.length}) with email '#{email}'" if candidates.length > 1 pers = candidates.first end end pers = @by_external_id[email&.downcase.strip]&.first if !pers && !email.to_s.strip.empty? pers end def find(object, strict: false) id = object.respond_to?("id")? object.send("id") : nil external_id = object.respond_to?("external_id")? object.send("external_id") : nil email = object.respond_to?("email")? object.send("email") : nil person(id: id, external_id: external_id, email: email, strict: strict) end def uniq(include_unsearchable: false) init_caches unsearchable = [] newFrom to_a.each_with_object([]) do |person, people| if found = find(person) people << found else unsearchable << person end end.tap do |people| people << unsearchable if include_unsearchable end end def exclude(object) exclude_people(into_a(object)) end def exclude_people(list) discarded = list.map do |person| find(person) end.compact newFrom to_a - discarded end def email_id_maps users.group_by(:email).transform_values { |person| person.id } end def group_by_supervisor to_h(:supervisor_id) end # only those that are present def supervisors h_all = self.to_h sup_ids = h_all.keys & self.supervisor_ids newFrom h_all.select do |id, person| sup_ids.include?(id) end.values end def missing_supervisors_ids sup_ids = self.supervisor_ids sup_ids - (sup_ids & self.ids) end def to_h(attr = "id") super(attr || "id") end protected def on_change @caches_init = false end private def init_caches return if @caches_init @by_id = to_h @by_external_id = to_h('external_id') @by_users_email = users.to_h('email') @by_non_users_email = non_users.to_h('email') @caches_init = true end end end end end