Skip to content

Commit

Permalink
Added core functionality for mulitple email addresses
Browse files Browse the repository at this point in the history
  • Loading branch information
tonyheadford committed Dec 6, 2021
1 parent f579637 commit 1931220
Show file tree
Hide file tree
Showing 21 changed files with 294 additions and 61 deletions.
30 changes: 19 additions & 11 deletions app/controllers/api/v1/ecf_participants_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,17 @@ def lead_provider

def participants
participant_profiles = ParticipantProfile::ECF.where(id: participant_profile_ids)
.joins(:user)
.joins(:participant_identity, :user)
.includes(
:participant_identity,
:user,
:cohort,
:school,
:ecf_participant_eligibility,
:ecf_participant_validation_data,
:schedule,
teacher_profile: { current_ect_profile: { mentor_profile: :user } },
)
)

if updated_since.present?
participant_profiles = participant_profiles.where(user: { updated_at: updated_since.. })
Expand All @@ -62,15 +63,7 @@ def participants
end

def participant_profile_ids
@participant_profile_ids ||= begin
inner_query = lead_provider.ecf_participant_profiles
.select("DISTINCT ON (participant_profiles.teacher_profile_id) teacher_profile_id, participant_profiles.status, participant_profiles.id as id")
.joins(:school_cohort)
.where(school_cohort: { cohort_id: Cohort.current.id })
.order(:teacher_profile_id, status: :asc)
.to_sql
ActiveRecord::Base.connection.query_values("SELECT id FROM (#{inner_query}) AS inner_query")
end
@participant_profile_ids ||= fetch_participant_profile_ids
end

def mentor_ids
Expand All @@ -82,6 +75,21 @@ def mentor_ids
.pluck(:id, User.arel_table["id"])
.to_h
end

def fetch_participant_profile_ids
# this retrieves the list of ECF participant profiles for the LeadProvider, one per
# teacher_profile (now participant_identity).
# Withdrawn profiles are included unless there is also an active one.
# The DISTINCT ON clause chooses the first record after ordering by status.
inner_query = lead_provider
.ecf_participant_profiles
.select("DISTINCT ON (participant_profiles.participant_identity_id) participant_identity_id, participant_profiles.status, participant_profiles.id AS id")
.joins(:school_cohort)
.where(school_cohort: { cohort_id: Cohort.current.id })
.order(:participant_identity_id, status: :asc)
.to_sql
ActiveRecord::Base.connection.query_values("SELECT id FROM (#{inner_query}) AS inner_query")
end
end
end
end
3 changes: 2 additions & 1 deletion app/controllers/users/sessions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ def mock_login
email = params.dig(:user, :email)
return unless TEST_DOMAINS.any? { |domain| email.include?(domain) }

user = User.find_by_email(email) || return
# user = User.find_by_email(email) || return
user = ParticipantIdentity.find_by(email: email)&.user || User.find_by(email: email) || return

sign_in(user, scope: :user)
redirect_to profile_dashboard_path(user)
Expand Down
16 changes: 16 additions & 0 deletions app/models/participant_identity.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# frozen_string_literal: true

class ParticipantIdentity < ApplicationRecord
belongs_to :user
has_many :participant_profiles

validates :email, presence: true, uniqueness: true, notify_email: true
validates :external_identifier, uniqueness: true

enum origin: {
ecf: "ecf",
npq: "npq",
}, _suffix: true

scope :superceded, -> { where("participant_identities.external_identifier != participant_identities.user_id") }
end
2 changes: 2 additions & 0 deletions app/models/participant_profile.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ class ParticipantProfile < ApplicationRecord

belongs_to :schedule, class_name: "Finance::Schedule", touch: true

belongs_to :participant_identity

has_one :user, through: :teacher_profile

has_many :validation_decisions, class_name: "ProfileValidationDecision"
Expand Down
2 changes: 2 additions & 0 deletions app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ class User < ApplicationRecord
devise :registerable, :trackable, :passwordless_authenticatable
has_paper_trail

has_many :participant_identities

has_one :induction_coordinator_profile, dependent: :destroy
has_many :schools, through: :induction_coordinator_profile

Expand Down
10 changes: 8 additions & 2 deletions app/serializers/participant_serializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,17 @@ def eligible_for_funding?(profile)
end

set_id :id do |profile|
profile.user.id
# NOTE: using this will retain the original ID exposed to provider
profile.participant_identity.external_identifier
# NOTE: use this instead to use new (de-duped) ID
# profile.user.id
end

active_participant_attribute :email do |profile|
profile.user.email
# NOTE: using this will retain the original email exposed to provider
profile.participant_identity.email
# NOTE: use this instead to use new (de-duped) email
# profile.user.email
end

active_participant_attribute :full_name do |profile|
Expand Down
21 changes: 21 additions & 0 deletions app/services/create_user_identity.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# frozen_string_literal: true

class CreateUserIdentity < BaseService
def call
ParticipantIdentity.find_or_create_by!(email: user.email) do |identity|
identity.user = user
identity.external_identifier = user.id
identity.email = user.email
identity.origin = origin
end
end

private

attr_accessor :user, :origin

def initialize(user:, origin: :ecf)
@user = user
@origin = origin
end
end
6 changes: 5 additions & 1 deletion app/services/early_career_teachers/create.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ def call
profile.school = school_cohort.school
end

ParticipantProfile::ECT.create!({ teacher_profile: teacher_profile, schedule: Finance::Schedule::ECF.default }.merge(ect_attributes)) do |profile|
ParticipantProfile::ECT.create!({
teacher_profile: teacher_profile,
schedule: Finance::Schedule::ECF.default,
participant_identity: CreateUserIdentity.call(user: user),
}.merge(ect_attributes)) do |profile|
ParticipantProfileState.create!(participant_profile: profile)

unless year_2020
Expand Down
6 changes: 5 additions & 1 deletion app/services/mentors/create.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,11 @@ def call
profile.school = school_cohort.school
end

ParticipantProfile::Mentor.create!({ teacher_profile: teacher_profile, schedule: Finance::Schedule::ECF.default }.merge(mentor_attributes)) do |mentor_profile|
ParticipantProfile::Mentor.create!({
teacher_profile: teacher_profile,
schedule: Finance::Schedule::ECF.default,
participant_identity: CreateUserIdentity.call(user: user)
}.merge(mentor_attributes)) do |mentor_profile|
ParticipantProfileState.create!(participant_profile: mentor_profile)
ParticipantMailer.participant_added(participant_profile: mentor_profile).deliver_later
mentor_profile.update_column(:request_for_details_sent_at, Time.zone.now)
Expand Down
1 change: 1 addition & 0 deletions app/services/npq/accept.rb
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ def create_profile
teacher_profile: teacher_profile,
school_urn: npq_application.school_urn,
school_ukprn: npq_application.school_ukprn,
participant_identity: CreateUserIdentity.call(user: user, origin: :npq),
) do |participant_profile|
ParticipantProfileState.find_or_create_by!(participant_profile: participant_profile)
end
Expand Down
23 changes: 23 additions & 0 deletions app/services/transfer_user_identity.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# frozen_string_literal: true

class TransferUserIdentity < BaseService
def call
from_user.participant_identities.each do |identity|
identity.update!(user: to_user)
identity.participant_profiles.each do |profile|
# NOTE: this might currently make the profile disappear from the ecf participants api endpoint
# because it limits one entry per teacher_profile
profile.update!(teacher_profile: to_user.teacher_profile)
end
end
end

private

attr_accessor :from_user, :to_user

def initialize(from_user:, to_user:)
@from_user = from_user
@to_user = to_user
end
end
16 changes: 16 additions & 0 deletions db/migrate/20211125163750_create_participant_identity.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# frozen_string_literal: true

class CreateParticipantIdentity < ActiveRecord::Migration[6.1]
def change
create_table :participant_identities do |t|
t.references :user, null: false, foreign_key: true
t.string :email, null: false
t.uuid :external_identifier, null: false
t.string :origin, null: false, default: "ecf"
t.timestamps
end

add_index :participant_identities, :email, unique: true
add_index :participant_identities, :external_identifier, unique: true
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# frozen_string_literal: true

class AddParticipantIdentityToParticipantProfile < ActiveRecord::Migration[6.1]
disable_ddl_transaction!

def change
add_reference :participant_profiles, :participant_identity, null: true, index: { algorithm: :concurrently }
end
end
15 changes: 15 additions & 0 deletions db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,18 @@
t.index ["user_id"], name: "index_participant_declarations_on_user_id"
end

create_table "participant_identities", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.uuid "user_id", null: false
t.string "email", null: false
t.uuid "external_identifier", null: false
t.string "origin", default: "ecf", null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["email"], name: "index_participant_identities_on_email", unique: true
t.index ["external_identifier"], name: "index_participant_identities_on_external_identifier", unique: true
t.index ["user_id"], name: "index_participant_identities_on_user_id"
end

create_table "participant_profile_schedules", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.uuid "participant_profile_id", null: false
t.uuid "schedule_id", null: false
Expand Down Expand Up @@ -577,10 +589,12 @@
t.datetime "request_for_details_sent_at"
t.string "training_status", default: "active", null: false
t.string "profile_duplicity", default: "single", null: false
t.uuid "participant_identity_id"
t.index ["cohort_id"], name: "index_participant_profiles_on_cohort_id"
t.index ["core_induction_programme_id"], name: "index_participant_profiles_on_core_induction_programme_id"
t.index ["mentor_profile_id"], name: "index_participant_profiles_on_mentor_profile_id"
t.index ["npq_course_id"], name: "index_participant_profiles_on_npq_course_id"
t.index ["participant_identity_id"], name: "index_participant_profiles_on_participant_identity_id"
t.index ["schedule_id"], name: "index_participant_profiles_on_schedule_id"
t.index ["school_cohort_id"], name: "index_participant_profiles_on_school_cohort_id"
t.index ["school_id"], name: "index_participant_profiles_on_school_id"
Expand Down Expand Up @@ -858,6 +872,7 @@
add_foreign_key "participant_declaration_attempts", "participant_declarations"
add_foreign_key "participant_declarations", "participant_profiles"
add_foreign_key "participant_declarations", "users"
add_foreign_key "participant_identities", "users"
add_foreign_key "participant_profile_schedules", "participant_profiles"
add_foreign_key "participant_profile_schedules", "schedules"
add_foreign_key "participant_profile_states", "participant_profiles"
Expand Down
Loading

0 comments on commit 1931220

Please sign in to comment.