Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: spotlights for 2024 edition #374

Merged
merged 7 commits into from
Feb 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ config :safira,
roulette_tokens_min: String.to_integer(System.get_env("ROULETTE_TOKENS_MIN") || "5"),
roulette_tokens_max: String.to_integer(System.get_env("ROULETTE_TOKENS_MAX") || "20"),
token_bonus: String.to_integer(System.get_env("TOKEN_BONUS") || "10"),
spotlight_duration: String.to_integer(System.get_env("SPOTLIGHT_DURATION") || "30"),
spotlight_duration: String.to_integer(System.get_env("SPOTLIGHT_DURATION") || "20"),
discord_bot_url: System.get_env("DISCORD_BOT_URL"),
discord_bot_api_key: System.get_env("DISCORD_BOT_API_KEY"),
discord_invite_url: System.get_env("DISCORD_INVITE_URL"),
Expand Down
2 changes: 2 additions & 0 deletions lib/safira/accounts/accounts.ex
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,8 @@ defmodule Safira.Accounts do

def get_company!(id), do: Repo.get!(Company, id)

def get_company_by_badge!(badge_id), do: Repo.get_by(Company, badge_id: badge_id)

def create_company(attrs \\ %{}) do
%Company{}
|> Company.changeset(attrs)
Expand Down
3 changes: 2 additions & 1 deletion lib/safira/accounts/company.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ defmodule Safira.Accounts.Company do
A company participating in SEI
"""
use Ecto.Schema

import Ecto.Changeset

alias Safira.Accounts.User

alias Safira.Contest.Badge

schema "companies" do
Expand All @@ -15,6 +15,7 @@ defmodule Safira.Accounts.Company do
field :channel_id, :string
field :remaining_spotlights, :integer
field :has_cv_access, :boolean

belongs_to :user, User
belongs_to :badge, Badge

Expand Down
16 changes: 13 additions & 3 deletions lib/safira/contest/contest.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,12 @@ defmodule Safira.Contest do

alias Ecto.Multi

alias Safira.Accounts
alias Safira.Accounts.Attendee
alias Safira.Contest.Badge
alias Safira.Contest.DailyToken
alias Safira.Contest.Redeem

alias Safira.Interaction

alias Safira.Repo

def list_badges do
Expand Down Expand Up @@ -278,9 +277,20 @@ defmodule Safira.Contest do

defp calculate_badge_tokens(badge) do
if Interaction.is_badge_spotlighted(badge.id) do
badge.tokens * 2
ceil(badge.tokens * get_multiplier(badge))
else
badge.tokens
end
end

defp get_multiplier(badge) do
company = Accounts.get_company_by_badge!(badge.id)

case company.sponsorship do
"Exclusive" -> 2
"Gold" -> 1.8
"Silver" -> 1.5
"Bronze" -> 1
end
end
end
23 changes: 10 additions & 13 deletions lib/safira/interaction/interaction.ex
Original file line number Diff line number Diff line change
Expand Up @@ -159,16 +159,18 @@ defmodule Safira.Interaction do
end

@doc """
Returns the only existing Spotlight
Returns the only existing spotlight
"""
def get_spotlight do
Repo.all(Spotlight) |> List.first()
end

@spotlight_duration Application.compile_env!(:safira, :spotlight_duration)

@doc """
Starts the spotlight
"""
def start_spotlight(company) do
def start_spotlight(company, duration \\ nil) do
Multi.new()
# get or build a spotlight
|> Multi.run(:get_spotlight, fn _repo, _changes ->
Expand All @@ -178,7 +180,10 @@ defmodule Safira.Interaction do
end)
# update the spotlight
|> Multi.insert_or_update(:upsert_spotlight, fn %{get_spotlight: spotlight} ->
Spotlight.changeset(spotlight, %{badge_id: company.badge_id, active: true})
Spotlight.changeset(spotlight, %{
badge_id: company.badge_id,
end: DateTime.utc_now() |> DateTime.add(duration || @spotlight_duration, :minute)
})
end)
# update company's remaining_spotlights
|> Multi.update(
Expand All @@ -191,19 +196,11 @@ defmodule Safira.Interaction do
|> apply_transaction()
end

@doc """
Signals spotlight as inactive
"""
def finish_spotlight do
get_spotlight()
|> Spotlight.finish_changeset(%{active: false})
|> Repo.update()
end

def is_badge_spotlighted(badge_id) do
spotlight = get_spotlight()

!is_nil(spotlight) and spotlight.active and spotlight.badge_id == badge_id
!is_nil(spotlight) and DateTime.compare(spotlight.end, DateTime.utc_now()) == :gt and
spotlight.badge_id == badge_id
end

defp apply_transaction(multi) do
Expand Down
34 changes: 17 additions & 17 deletions lib/safira/interaction/spotlight.ex
Original file line number Diff line number Diff line change
@@ -1,40 +1,40 @@
defmodule Safira.Interaction.Spotlight do
@moduledoc """
Spotlight was a feature that would highlight a company
for a given amount of time in discord
(Deprecated, used for online SEI)
Spotlight allows companies to highlight a badge for a period of time,
giving more tokens to attendees who redeem it.
"""
use Ecto.Schema

import Ecto.Changeset

alias Safira.Interaction

schema "spotlights" do
field :active, :boolean, default: false
field :end, :utc_datetime
field :badge_id, :id

field :lock_version, :integer, default: 1

timestamps()
end

@doc false
def changeset(spotlight, attrs) do
spotlight
|> cast(attrs, [:active, :badge_id])
|> validate_required([:active, :badge_id])
|> cast(attrs, [:end, :badge_id])
|> validate_required([:end, :badge_id])
|> validate_not_already_active()
|> Ecto.Changeset.optimistic_lock(:lock_version)
end

def finish_changeset(spotlight, attrs) do
spotlight
|> cast(attrs, [:active])
|> validate_required([:active])
end

defp validate_not_already_active(changeset) do
if changeset.data && changeset.data.active do
add_error(changeset, :active, "Another spotlight is still active")
else
changeset
if changeset.data do
spotlight = Interaction.get_spotlight()

if spotlight && DateTime.compare(spotlight.end, DateTime.utc_now()) == :gt do
add_error(changeset, :end, "Another spotlight is still active")
else
changeset
end
end
end
end
70 changes: 70 additions & 0 deletions lib/safira_web/controllers/spotlight/spotlight_controller.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
defmodule SafiraWeb.SpotlightController do
use SafiraWeb, :controller

alias Safira.Accounts
alias Safira.Interaction

action_fallback SafiraWeb.FallbackController

def index(conn, _params) do
is_admin = Accounts.is_admin(conn)

if is_admin do
companies = Accounts.list_companies()
spotlight = Interaction.get_spotlight()

conn
|> put_status(:ok)
|> render(:index, companies: companies, spotlight: spotlight)
else
conn
|> put_status(:unauthorized)
|> json(%{error: "Cannot access resource"})
|> halt()
end
end

def current(conn, _params) do
is_company = Accounts.is_company(conn)
spotlight = Interaction.get_spotlight()

if is_company do
conn
|> put_status(:unauthorized)
|> json(%{error: "Cannot access resource"})
|> halt()
else
if !spotlight || DateTime.compare(spotlight.end, DateTime.utc_now()) == :lt do
conn
|> put_status(:not_found)
|> json(%{error: "Spotlight not found"})
|> halt()
else
company = Accounts.get_company_by_badge!(spotlight.badge_id)

conn
|> put_status(:ok)
|> render(:current, company: company, spotlight: spotlight)
end
end
end

def create(conn, %{"company_id" => company_id} = params) do
is_admin = Accounts.is_admin(conn)

if is_admin do
company = Accounts.get_company!(company_id)

with {:ok, _struct} <- Interaction.start_spotlight(company, params["duration"]) do
conn
|> put_status(:created)
|> json(%{spotlight: "Spotlight created successfully"})
end
else
conn
|> put_status(:unauthorized)
|> json(%{error: "Cannot access resource"})
|> halt()
end
end
end
38 changes: 38 additions & 0 deletions lib/safira_web/controllers/spotlight/spotlight_json.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
defmodule SafiraWeb.SpotlightJSON do
@moduledoc false

def index(%{companies: companies, spotlight: spotlight}) do
%{
data: for(company <- companies, do: data(company, spotlight))
}
end

def current(%{company: company, spotlight: spotlight}) do
data =
data(company, spotlight)
|> Map.drop([:remaining])
|> Map.put(:badge_id, company.badge_id)

%{
data: data
}
end

defp data(company, spotlight) do
default = default(company)

if spotlight && spotlight.badge_id == company.badge_id do
Map.put(default, :end, spotlight.end)
else
default
end
end

defp default(company) do
%{
id: company.id,
name: company.name,
remaining: company.remaining_spotlights
}
end
end
57 changes: 0 additions & 57 deletions lib/safira_web/controllers/spotlight_controller.ex

This file was deleted.

6 changes: 5 additions & 1 deletion lib/safira_web/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,11 @@ defmodule SafiraWeb.Router do

post "/roulette", RouletteController, :spin
post "/give_bonus/:id", BonusController, :give_bonus
post "/spotlight", SpotlightController, :create

get "/spotlights", SpotlightController, :index
get "/spotlight", SpotlightController, :current
post "/spotlight/:company_id", SpotlightController, :create

post "/store/redeem", DeliverRedeemableController, :create
post "/roulette/redeem", DeliverPrizeController, :create

Expand Down
3 changes: 2 additions & 1 deletion priv/repo/migrations/20210218152530_create_spotlights.exs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ defmodule Safira.Repo.Migrations.CreateSpotlights do

def change do
create table(:spotlights) do
add :active, :boolean, default: false, null: false
add :end, :utc_datetime
add :badge_id, references(:badges, on_delete: :nothing)

add :lock_version, :integer, default: 1

timestamps()
Expand Down
3 changes: 2 additions & 1 deletion test/factories/accounts_factory.ex
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@ defmodule Safira.AccountsFactory do

%Safira.Accounts.Company{
name: name,
sponsorship: Enum.random(["exclusive", "gold", "silver", "bronze"]),
sponsorship: Enum.random(["Exclusive", "Gold", "Silver", "Bronze"]),
channel_id: String.downcase(name),
remaining_spotlights: Enum.random(1..3),
user: build(:user),
badge: build(:badge),
has_cv_access: Enum.random([true, false])
Expand Down
Loading
Loading