Packages
ami
0.3.2
0.3.9
0.3.8
retired
0.3.7
retired
0.3.6
retired
0.3.5
retired
0.3.4
retired
0.3.3
retired
0.3.2
retired
0.3.1
retired
0.3.0
retired
0.2.9
retired
0.2.8
retired
0.2.7
retired
0.2.6
retired
0.2.5
retired
0.2.3
retired
0.2.2
retired
0.2.1
retired
0.2.0
retired
0.1.9
retired
0.1.8
retired
0.1.7
retired
0.1.6
retired
0.1.5
retired
0.1.4
retired
Package containing all AMI Models shared between micro-services.
Retired package: Security issue - retiring
Current section
Files
Jump to
Current section
Files
lib/ami/user.ex
defmodule Ami.User do
use Ecto.Schema
# use Bitwise
import Ecto.{
Query,
Changeset
}
alias Ami.{
Company,
Profile,
Course,
Enrollment,
Score,
CommunityUser,
TemplateResult,
Job,
Repo,
RecentActivity,
Role,
Comment,
Post,
DownloadedTool,
DownloadedResource,
Buddyship
}
schema "users" do
@timestamps_opts [type: :naive_datetime_usec]
field(:name, :string)
field(:email, :string)
field(:encrypted_password, :string)
field(:reset_password_token, :string)
field(:reset_password_sent_at, :utc_datetime_usec)
field(:remember_created_at, :utc_datetime_usec)
field(:sign_in_count, :integer)
field(:current_sign_in_at, :utc_datetime_usec)
field(:last_sign_in_at, :utc_datetime_usec)
field(:current_sign_in_ip, :string)
field(:last_sign_in_ip, :string)
field(:confirmation_token, :string)
field(:confirmed_at, :utc_datetime_usec)
field(:confirmation_sent_at, :utc_datetime_usec)
field(:unconfirmed_email, :string)
field(:created_at, :utc_datetime_usec)
field(:updated_at, :utc_datetime_usec)
field(:admin, :boolean)
field(:invitation_token, :string)
field(:invitation_created_at, :utc_datetime_usec)
field(:invitation_sent_at, :utc_datetime_usec)
field(:invitation_accepted_at, :utc_datetime_usec)
field(:invitation_limit, :integer)
field(:invited_by_id, :integer)
field(:invited_by_type, :string)
field(:invitations_count, :integer)
field(:status, :integer)
field(:accountibility_partner_for, :integer)
field(:roles_mask, :integer)
field(:renewable_date, :date)
field(:job_matching, :boolean)
field(:downloaded_tools_count, :integer)
field(:downloaded_resources_count, :integer)
field(:sign_in_streak, :integer)
field(:yalie_authtoken, :string)
field(:group_label, :string)
field(:is_dormant, :boolean)
field(:session_id, :string)
field(:uuid, :binary)
field(:with_updated_password, :boolean)
field(:password, :string, virtual: true)
field(:jwt, :binary)
field(:is_verified, :boolean)
field(:api_user, :boolean)
field(:current_password, :string, virtual: true)
belongs_to(:company, Company)
has_one(:administered_course, Course, foreign_key: :course_admin_id)
has_one(:profile, Profile, on_delete: :delete_all, on_replace: :update)
has_many(:posts, Post, foreign_key: :author_id)
has_many(:comments, Comment)
has_many(:enrollments, Enrollment)
has_many(:course_periods, through: [:enrollments, :course_period])
has_many(:courses, through: [:course_periods, :course])
has_many(:scores, Score)
has_many(:community_users, CommunityUser)
has_many(:communities, through: [:community_users, :community])
has_many(:template_results, TemplateResult)
has_many(:recent_activities, RecentActivity, on_delete: :delete_all)
has_many(:downloaded_resources, DownloadedResource)
has_many(:downloaded_tools, DownloadedTool)
has_many(:assigned_buddyships, Buddyship, foreign_key: :buddy_id)
end
def changeset(%__MODULE__{} = struct, params \\ %{}) do
struct
|> cast(params, [])
end
def pwd_changeset(user, attrs) do
user
|> cast(attrs, [:password])
|> validate_confirmation(:password, message: "does not match password")
|> put_password_hash()
end
def roles_changeset(user, attrs) do
user
|> cast(attrs, [:roles_mask])
end
def pwd_reset_changeset(user, attrs) do
user
|> cast(attrs, [:name, :password])
|> validate_confirmation(:password, message: "does not match password")
|> put_password_hash()
end
def update_pwd_changeset(user, attrs) do
user
|> cast(attrs, [:with_updated_password])
|> validate_required([:with_updated_password])
end
def update_jwt_changeset(user, attrs) do
user
|> cast(attrs, [:jwt])
|> validate_required([:jwt])
end
def update_current_sign_in_at_changeset(user, attrs) do
user
|> cast(attrs, [:current_sign_in_at])
|> validate_required([:current_sign_in_at])
end
def update_current_sign_in_ip_changeset(user, attrs) do
user
|> cast(attrs, [:current_sign_in_ip])
|> validate_required([:current_sign_in_ip])
end
def update_last_sign_in_at_changeset(user, attrs) do
user
|> cast(attrs, [:last_sign_in_at])
|> validate_required([:last_sign_in_at])
end
def update_last_sign_in_ip_changeset(user, attrs) do
user
|> cast(attrs, [:last_sign_in_ip])
|> validate_required([:last_sign_in_ip])
end
def increment_sign_in_count_changeset(user, attrs) do
user
|> cast(attrs, [:sign_in_count])
|> validate_required([:sign_in_count])
end
def increment_sign_in_streak_changeset(user, attrs) do
user
|> cast(attrs, [:sign_in_streak])
|> validate_required([:sign_in_streak])
end
def registration_changeset(user, attrs \\ %{}) do
attrs =
if Map.has_key?(attrs, "profile") do
p_attrs = Map.put_new(attrs["profile"], "phone_cc", attrs["phone_cc"])
Map.put(attrs, "profile", p_attrs)
else
attrs
end
user
|> cast(attrs, [:name, :email, :password])
|> validate_confirmation(:password, message: "does not match password")
|> validate_required([:name, :email])
|> unique_constraint(:name, name: :users_name_index)
|> unique_constraint(:email,
name: :index_users_on_email,
message: "Email has already been used to create an account"
)
|> cast_assoc(:profile)
|> put_password_hash()
end
def no_email_registration_changeset(user, attrs \\ %{}) do
p_attrs = Map.put_new(attrs["profile"], "phone_cc", attrs["phone_cc"])
attrs = Map.put(attrs, "profile", p_attrs)
user
|> cast(attrs, [:name, :email, :password])
|> validate_confirmation(:password, message: "does not match password")
|> validate_required([:name])
|> unique_constraint(:name)
|> cast(%{email: generate_temporary_email(attrs["name"])}, [:email])
|> unique_constraint(:email)
|> cast_assoc(:profile)
|> put_password_hash()
end
def email_registration_changeset(user, attrs) do
user
|> cast(attrs, [:email])
|> validate_required([:email])
|> unique_constraint(:email)
end
def phone_changeset(user, attrs) do
user
|> cast(attrs, [])
|> cast_assoc(:profile)
end
def edit_phone_changeset(user, attrs) do
attrs =
if Map.has_key?(attrs, "profile") do
p_attrs = Map.put_new(attrs["profile"], "phone_cc", attrs["phone_cc"])
Map.put(attrs, "profile", p_attrs)
else
attrs
end
user
|> cast(attrs, [])
|> cast_assoc(:profile, with: &Ami.Profile.edit_phone_changeset/2)
end
def update_is_verified_changeset(user, attrs) do
user
|> cast(attrs, [:is_verified])
|> validate_required([:is_verified])
end
def set_reset_pwd_token_changeset(user, attrs) do
user
|> cast(attrs, [:reset_password_token])
|> validate_required([:reset_password_token])
end
def set_invitation_token_changeset(user, attrs) do
user
|> cast(attrs, [:invitation_token])
end
def profile_settings_changeset(user, attrs) do
attrs =
if Map.has_key?(attrs, "profile") do
p_attrs = Map.put_new(attrs["profile"], "phone_cc", attrs["phone_cc"])
Map.put(attrs, "profile", p_attrs)
else
attrs
end
user
|> cast(attrs, [:name, :email, :password, :is_dormant, :status, :current_password])
|> unique_constraint(:name)
|> unique_constraint(:email)
|> validate_confirmation(:password, message: "does not match password")
|> validate_required([:current_password])
|> check_current_password_validity(user)
|> cast_assoc(:profile, with: &Profile.settings_changeset/2)
|> put_password_hash()
end
def profile_privacy_notify_changeset(user, attrs) do
user
|> cast(attrs, [:name, :email, :password, :is_dormant, :status, :current_password])
|> cast_assoc(:profile, with: &Profile.privacy_notify_settings_changeset/2)
end
def profile_wizard_changeset(user, attrs) do
attrs =
if Map.has_key?(attrs, "profile") do
p_attrs = Map.put_new(attrs["profile"], "phone_cc", attrs["phone_cc"])
Map.put(attrs, "profile", p_attrs)
else
attrs
end
user
|> cast(attrs, [:name, :email, :password, :is_dormant, :status, :current_password])
|> unique_constraint(:name)
|> unique_constraint(:email)
|> validate_confirmation(:password, message: "does not match password")
# |> validate_required([:current_password])
# |> check_current_password_validity(user)
|> cast_assoc(:profile, with: &Profile.full_changeset/2)
|> put_password_hash()
end
def profile_wizard_currentjob_changeset(user, attrs) do
user
|> cast(attrs, [:name, :email, :password, :is_dormant, :status, :current_password])
|> cast_assoc(:profile, with: &Profile.full_changeset/2)
end
defp put_password_hash(
%Ecto.Changeset{valid?: true, changes: %{password: password}} = changeset
) do
change(changeset, encrypted_password: Argon2.hash_pwd_salt(password))
end
defp put_password_hash(changeset), do: changeset
defp generate_temporary_email(name) do
"#{name}@tempafricanmanagers.org"
end
defp check_current_password_validity(changeset, user) do
case Argon2.check_pass(user, changeset.changes[:current_password]) do
{:error, _} ->
add_error(changeset, :current_password, "wrong current password")
_ ->
changeset
end
end
def membership_status(status) do
case status do
0 -> "Non-member"
1 -> "Member"
2 -> "Business member"
_ -> status
end
end
def get_nonmembers() do
__MODULE__ |> Ecto.Query.where(status: 0)
end
def get_nonmembers(query) do
query |> Ecto.Query.where(status: 0)
end
def get_members() do
__MODULE__ |> Ecto.Query.where(status: 1)
end
def get_members(query) do
query |> Ecto.Query.where(status: 1)
end
def get_business_members() do
__MODULE__ |> Ecto.Query.where(status: 2)
end
def get_business_members(query) do
query |> Ecto.Query.where(status: 2)
end
def or_get_members(query) do
query |> Ecto.Query.or_where(status: 1)
end
def or_get_business_members(query) do
query |> Ecto.Query.or_where(status: 2)
end
def awaken do
__MODULE__ |> Ecto.Query.where(is_dormant: false)
end
def awaken(query) do
query |> Ecto.Query.where(is_dormant: false)
end
def icoach_progress_for(user) do
progress =
%{pcentage: 0, completed_goals: nil}
|> has_started_self_assessment(user)
|> has_completed_self_assessment(user)
|> has_started_360(user)
|> has_completed_360(user)
|> calculate_progress_percentage
progress
end
def has_started_self_assessment(progress_map, user) do
count =
Repo.one(
from(
tp in TemplateResult,
where: tp.user_id == ^user.id,
where: is_nil(tp.owner_result_id),
select: count(tp.id)
)
)
cond do
count > 0 -> Map.put_new(progress_map, :started_self_assessment, true)
count == 0 -> Map.put_new(progress_map, :started_self_assessment, false)
end
end
def has_completed_self_assessment(progress_map, user) do
count =
Repo.one(
from(
tp in TemplateResult,
where: tp.user_id == ^user.id,
where: tp.status == 1,
where: is_nil(tp.owner_result_id),
select: count(tp.id)
)
)
cond do
count > 0 -> Map.put_new(progress_map, :completed_self_assessment, true)
count == 0 -> Map.put_new(progress_map, :completed_self_assessment, false)
end
end
def has_started_360(progress_map, user) do
started =
TemplateResult.get_done_self_assessments_for(user)
|> Enum.any?(fn e -> length(e.respondent_results) > 0 end)
Map.put_new(progress_map, :started_360, started)
end
def has_completed_360(progress_map, user) do
completed =
TemplateResult.get_done_self_assessments_for(user)
|> Enum.any?(fn e -> length(e.respondent_results) >= 3 end)
Map.put_new(progress_map, :completed_360, completed)
end
def calculate_progress_percentage(progress_map) do
progress_map =
if progress_map[:started_self_assessment],
do: Map.update!(progress_map, :pcentage, &(&1 + 15)),
else: progress_map
progress_map =
if progress_map[:completed_self_assessment],
do: Map.update!(progress_map, :pcentage, &(&1 + 15)),
else: progress_map
progress_map =
if progress_map[:started_360],
do: Map.update!(progress_map, :pcentage, &(&1 + 15)),
else: progress_map
progress_map =
if progress_map[:completed_360],
do: Map.update!(progress_map, :pcentage, &(&1 + 20)),
else: progress_map
progress_map
end
def preload_jobs(user_struct) do
if Ecto.assoc_loaded?(user_struct.profile) do
Repo.preload(
user_struct.profile,
jobs: from(j in Job, where: j.current == true, select: [:role])
).jobs
else
Repo.preload(
user_struct,
profile: [jobs: from(j in Job, where: j.current == true), select: [:role]]
).profile.jobs
end
end
def roles do
[
:admin,
:instructor,
:community_moderator,
:course_admin,
:learning_facilitator,
:buddy,
:accountibility_partner,
:respondent,
:system_admin
]
end
def can_access_admin_panel(user_struct) do
grants = MapSet.new([:admin, :community_moderator, :course_admin])
if user_struct.roles_mask != nil do
user_roles =
Role.roles_from_mask(user_struct.roles_mask)
|> Enum.into(MapSet.new())
common_items = MapSet.intersection(grants, user_roles)
if MapSet.size(common_items) > 0, do: true, else: false
else
false
end
end
def can_manage_user(user_struct) do
has_role?(user_struct, :admin)
end
def has_role?(user_struct, role) do
Role.has_role?(user_struct, role)
end
def user_name(user_struct) do
user = Repo.preload(user_struct, :profile)
cond do
!profile_blank?(user.profile) ->
user.profile.first_name
profile_blank?(user.profile) && !name_blank?(user) ->
user.name
true ->
hd(String.split(user.email, "@"))
end
end
def user_message_name(user_struct, term) do
user = Repo.preload(user_struct, :profile)
if Regex.match?(~r/\w+@[\w+.]+/i, term) do
term
else
cond do
!profile_blank?(user.profile) ->
"#{user.profile.first_name} #{user.profile.last_name}"
profile_blank?(user.profile) && !name_blank?(user) ->
user.name
true ->
hd(String.split(user.email, "@"))
end
end
end
def user_message_name(user_struct) do
user = Repo.preload(user_struct, :profile)
cond do
!profile_blank?(user.profile) ->
"#{user.profile.first_name} #{user.profile.last_name}"
profile_blank?(user.profile) && !name_blank?(user) ->
user.name
true ->
hd(String.split(user.email, "@"))
end
end
def user_tag_name(user_struct) do
user = Repo.preload(user_struct, :profile)
cond do
!profile_blank?(user.profile) ->
"#{String.replace(user.profile.first_name, " ", "")}#{
String.replace(user.profile.last_name, " ", "")
}"
profile_blank?(user.profile) && !name_blank?(user) ->
String.replace(user.name, " ", "")
true ->
hd(String.split(user.email, "@"))
end
end
defp profile_blank?(profile) do
is_nil(profile) || profile.first_name == "" || is_nil(profile.first_name)
end
defp name_blank?(user) do
is_nil(user.name) || user.name == ""
end
def potential_msg_recipients_for(user, term) do
enr_query =
from(
e in Ami.Enrollment,
join: cp in assoc(e, :course_period)
)
user = Repo.preload(user, [:communities, [enrollments: {enr_query, [:course_period]}]])
# usernames =
cond do
!Role.has_role?(user, :community_moderator) &&
(Role.has_role?(user, :admin) || Role.has_role?(user, :system_admin)) ->
profile_query =
from(
p in Ami.Profile,
select: [:first_name, :last_name]
)
query =
from(
u in __MODULE__,
left_join: p in assoc(u, :profile),
select: [:id, :email, :name],
where: u.email == ^term,
or_where: ilike(u.name, ^"#{term}%"),
or_where: ilike(p.first_name, ^"#{term}%"),
or_where: ilike(p.last_name, ^"#{term}%"),
preload: [profile: ^profile_query]
)
Repo.all(query)
|> Enum.map(fn user ->
%{label: __MODULE__.user_message_name(user, term), value: user.id}
end)
|> List.insert_at(0, %{label: "All non-members", value: "allnonmembers"})
|> List.insert_at(0, %{label: "All members", value: "allmembers"})
|> List.insert_at(0, %{label: "All business members", value: "allbusinessmembers"})
|> List.insert_at(0, %{label: "All users", value: "allusers"})
Role.has_role?(user, :community_moderator) &&
(!Role.has_role?(user, :admin) && !Role.has_role?(user, :system_admin)) ->
community_recipients =
Enum.flat_map(user.communities, fn community ->
Ami.Community.other_community_members(community, user)
end)
|> Enum.uniq()
course_recipients =
Enum.flat_map(user.enrollments, fn enrollment ->
Ami.Enrollment.other_cp_students(enrollment.course_period, user)
end)
|> Enum.uniq()
tmp_usernames =
Enum.concat(community_recipients, course_recipients)
|> Enum.uniq()
|> Enum.map(fn user ->
%{
label: __MODULE__.user_message_name(user, term),
value: user.id
}
end)
__MODULE__.moderated_communities(user)
|> Enum.map(fn community ->
%{
label: "All #{community.title} members",
value: "community_#{community.id}"
}
end)
|> Enum.into(tmp_usernames)
Role.has_role?(user, :community_moderator) &&
(Role.has_role?(user, :admin) || Role.has_role?(user, :system_admin)) ->
profile_query =
from(
p in Ami.Profile,
select: [:first_name, :last_name]
)
query =
from(
u in __MODULE__,
left_join: p in assoc(u, :profile),
select: [:id, :email, :name],
where: u.email == ^term,
or_where: ilike(u.name, ^"#{term}%"),
or_where: ilike(p.first_name, ^"#{term}%"),
or_where: ilike(p.last_name, ^"#{term}%"),
preload: [profile: ^profile_query]
)
tmp_usernames =
Repo.all(query)
|> Enum.map(fn user ->
%{label: __MODULE__.user_message_name(user, term), value: user.id}
end)
|> List.insert_at(0, %{label: "All non-members", value: "allnonmembers"})
|> List.insert_at(0, %{label: "All members", value: "allmembers"})
|> List.insert_at(0, %{label: "All business members", value: "allbusinessmembers"})
|> List.insert_at(0, %{label: "All users", value: "allusers"})
__MODULE__.moderated_communities(user)
|> Enum.map(fn community ->
%{label: "All #{community.title} members", value: "community_#{community.id}"}
end)
|> Enum.into(tmp_usernames)
true ->
community_recipients =
Enum.flat_map(user.communities, fn community ->
Ami.Community.other_community_members(community, user)
end)
|> Enum.uniq()
course_recipients =
Enum.flat_map(user.enrollments, fn enrollment ->
Ami.Enrollment.other_cp_students(enrollment.course_period, user)
end)
|> Enum.uniq()
Enum.concat(community_recipients, course_recipients)
|> Enum.uniq()
|> Enum.map(fn user ->
%{label: __MODULE__.user_message_name(user, term), value: user.id}
end)
end
end
def moderated_communities(user) do
from(
c in Ami.Community,
join: cu in assoc(c, :community_users),
where: cu.user_id == ^user.id,
where: cu.is_moderator == true
)
|> Repo.all()
end
def get_users_for_digest(type, frequency) do
case type do
:nonmember ->
get_users_with_digest_settings(0, frequency)
:member ->
get_users_with_digest_settings(1, frequency)
:business_member ->
get_users_with_digest_settings(2, frequency)
_ ->
[]
end
end
def get_users_for_long_absence_notice() do
Enum.map([4, 6, 8], fn weeks ->
q =
__MODULE__.awaken()
|> last_sign_in_at_since(weeks)
|> email_only()
Repo.all(q)
end)
|> List.flatten()
end
def last_sign_in_at_since(query, weeks) when is_integer(weeks) do
start_boundary =
Timex.now()
|> Timex.shift(weeks: -weeks)
|> Timex.beginning_of_day()
end_boundary =
Timex.now()
|> Timex.shift(weeks: -(weeks + 1))
|> Timex.beginning_of_day()
from(u in query,
where: u.last_sign_in_at <= ^start_boundary,
where: u.last_sign_in_at > ^end_boundary
)
end
def email_only(query) do
from(u in query, select: u.email)
end
defmacro has_attribute(field, key, value) do
quote do
fragment("?::hstore -> ? = ?", unquote(field), unquote(key), unquote(value))
end
end
def get_users_with_digest_settings(status, frequency) do
from(
u in __MODULE__,
join: p in assoc(u, :profile),
where: u.status == ^status,
where: u.is_dormant == false,
where: has_attribute(p.settings, "notify", ^frequency),
select: [:id, :email, :is_dormant, :status]
)
|> Repo.all()
end
def potential_oneforall_buddies(term) do
query =
from(
u in __MODULE__,
left_join: p in assoc(u, :profile),
select: [:id, :email, :name],
where: u.email == ^term,
or_where: ilike(u.email, ^"#{term}%")
)
usernames =
Repo.all(query)
|> Enum.map(fn user ->
%{label: user.email, value: user.id}
end)
end
end