Skip to content
Open
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
134 changes: 104 additions & 30 deletions registrations/lib/registrations/waydowntown.ex
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,19 @@ defmodule Registrations.Waydowntown do
Map.keys(concepts_yaml())
end

def concept_is_long_running(concept) do
concept_flag?(concept, "long_running")
end

def concept_is_placed(concept) do
!(concepts_yaml()[concept]["placeless"] == true)
concept_data = Map.get(concepts_yaml(), concept, %{})
concept_data["placeless"] != true
end

defp concept_flag?(concept, key) do
concepts_yaml()
|> Map.get(concept, %{})
|> Map.get(key) == true
end

def list_regions do
Expand Down Expand Up @@ -153,7 +164,7 @@ defmodule Registrations.Waydowntown do
{:error, "No placed specification available"}

specification ->
{:ok, Run.changeset(%Run{}, Map.put(attrs, "specification_id", specification.id))}
{:ok, build_run_changeset(attrs, specification)}
end
end

Expand All @@ -163,7 +174,7 @@ defmodule Registrations.Waydowntown do
{:error, "No specification found near the specified position"}

specification ->
{:ok, Run.changeset(%Run{}, Map.put(attrs, "specification_id", specification.id))}
{:ok, build_run_changeset(attrs, specification)}
end
end

Expand All @@ -178,7 +189,7 @@ defmodule Registrations.Waydowntown do
{:error, "No specification with the specified concept available"}

specification ->
{:ok, Run.changeset(%Run{}, Map.put(attrs, "specification_id", specification.id))}
{:ok, build_run_changeset(attrs, specification)}
end
end
end
Expand All @@ -189,7 +200,7 @@ defmodule Registrations.Waydowntown do
{:error, "Specification not found"}

specification ->
{:ok, Run.changeset(%Run{}, Map.put(attrs, "specification_id", specification.id))}
{:ok, build_run_changeset(attrs, specification)}
end
end

Expand All @@ -209,7 +220,23 @@ defmodule Registrations.Waydowntown do
task_description: concept_data["instructions"]
}),
{:ok, _} <- create_answers(specification, answers) do
{:ok, Run.changeset(%Run{}, Map.put(attrs, "specification_id", specification.id))}
{:ok, build_run_changeset(attrs, specification)}
end
end

defp build_run_changeset(attrs, %Specification{concept: concept} = specification) do
attrs = Map.put(attrs, "specification_id", specification.id)

%Run{}
|> Run.changeset(attrs)
|> maybe_start_long_running_run(concept)
end

defp maybe_start_long_running_run(changeset, concept) do
if concept_is_long_running(concept) do
Ecto.Changeset.put_change(changeset, :started_at, DateTime.utc_now())
else
changeset
end
end

Expand Down Expand Up @@ -303,14 +330,14 @@ defmodule Registrations.Waydowntown do
end

def list_specifications do
Specification |> Repo.all() |> Repo.preload(region: [parent: [parent: [:parent]]])
Specification |> Repo.all() |> Repo.preload(answers: [:region], region: [parent: [parent: [:parent]]])
end

def list_specifications_for(user) do
from(i in Specification)
|> where([i], i.creator_id == ^user.id)
|> Repo.all()
|> Repo.preload(answers: [:reveals], region: [parent: [parent: [:parent]]])
|> Repo.preload(answers: [:reveals, :region], region: [parent: [parent: [:parent]]])
end

def get_specification!(id), do: Repo.get!(Specification, id)
Expand Down Expand Up @@ -386,28 +413,32 @@ defmodule Registrations.Waydowntown do
end

defp check_submission_validity(current_user_id, run, submission_text, answer_id) do
case run.specification.concept do
"string_collector" ->
check_for_duplicate_normalised_submission(current_user_id, run, submission_text)
concept = run.specification.concept

cond do
concept in ["string_collector", "payphone_collector", "elevator_collector"] ->
check_for_duplicate_normalised_submission(current_user_id, run, submission_text, concept)

concept when concept in ["food_court_frenzy", "fill_in_the_blank", "count_the_items"] ->
concept in ["food_court_frenzy", "fill_in_the_blank", "count_the_items"] ->
check_for_paired_answer(current_user_id, run, answer_id)

concept when concept in ["orientation_memory", "cardinal_memory"] ->
concept in ["orientation_memory", "cardinal_memory"] ->
check_for_ordered_answer(current_user_id, run, answer_id)

_ ->
true ->
{:ok, nil}
end
end

defp check_for_duplicate_normalised_submission(current_user_id, run, submission_text) do
normalized_submission = normalize_string(submission_text)
defp check_for_duplicate_normalised_submission(current_user_id, run, submission_text, concept) do
normalised_submission = normalise_submission_for_concept(concept, submission_text)

existing_submissions =
Enum.map(Enum.filter(run.submissions, &(&1.creator_id == current_user_id)), &normalize_string(&1.submission))
run.submissions
|> Enum.filter(&(&1.creator_id == current_user_id))
|> Enum.map(&normalise_submission_for_concept(concept, &1.submission))

if normalized_submission in existing_submissions do
if normalised_submission in existing_submissions do
{:error, "Submission already submitted"}
else
{:ok, nil}
Expand Down Expand Up @@ -491,8 +522,10 @@ defmodule Registrations.Waydowntown do
defp run_expired?(_), do: false

def get_run_progress(run, current_user_id) do
concept = run.specification.concept

correct_submissions =
if run.specification.concept in ["orientation_memory", "cardinal_memory"] do
if concept in ["orientation_memory", "cardinal_memory"] do
latest_submission =
run.submissions
|> Enum.filter(&(&1.creator_id == current_user_id))
Expand Down Expand Up @@ -547,15 +580,19 @@ defmodule Registrations.Waydowntown do
submission_text,
_answer_id
)
when concept in ["bluetooth_collector", "code_collector", "string_collector"] do
normalized_answer = if concept == "string_collector", do: normalize_string(submission_text), else: submission_text
when concept in [
"bluetooth_collector",
"code_collector",
"string_collector",
"payphone_collector",
"elevator_collector"
] do
normalised_submission = normalise_submission_for_concept(concept, submission_text)

correct_answers =
if concept == "string_collector",
do: Enum.map(answers, &normalize_string(&1.answer)),
else: Enum.map(answers, & &1.answer)
Enum.map(answers, &normalize_answer_for_concept(concept, &1.answer))

normalized_answer in correct_answers
normalised_submission in correct_answers
end

defp check_submission_correctness(
Expand Down Expand Up @@ -602,7 +639,15 @@ defmodule Registrations.Waydowntown do
"count_the_items" ->
true

concept when concept in ["bluetooth_collector", "code_collector", "string_collector", "food_court_frenzy"] ->
concept
when concept in [
"bluetooth_collector",
"code_collector",
"string_collector",
"payphone_collector",
"elevator_collector",
"food_court_frenzy"
] ->
run_answer_count = length(run.specification.answers)
correct_submissions = Enum.filter(run.submissions, & &1.correct)
correct_submissions_by_user = Enum.group_by(correct_submissions, & &1.creator_id)
Expand Down Expand Up @@ -632,6 +677,26 @@ defmodule Registrations.Waydowntown do
|> String.downcase()
end

defp normalise_submission_for_concept(concept, submission) do
if concept_normalizes_submissions?(concept) do
normalize_string(submission)
else
submission
end
end

defp normalize_answer_for_concept(concept, answer) do
if concept_normalizes_submissions?(concept) do
normalize_string(answer)
else
answer
end
end

defp concept_normalizes_submissions?(concept) do
concept_flag?(concept, "normalise_submissions") or concept == "string_collector"
end

def start_run(current_user, %Run{} = run) do
run_has_current_user_participation = run.participations |> Enum.map(& &1.user_id) |> Enum.member?(current_user.id)

Expand All @@ -652,14 +717,23 @@ defmodule Registrations.Waydowntown do

defp run_preloads do
[
participations: [run: [:participations, specification: [answers: [:reveals]], submissions: [answer: [:reveals]]]],
submissions: [answer: [:reveals]],
specification: [answers: [:reveals], region: [parent: [parent: [:parent]]]]
participations: [
run: [
:participations,
specification: [answers: [:reveals, :region]],
submissions: [answer: [:reveals, :region]]
]
],
submissions: [answer: [:reveals, :region]],
specification: [answers: [:reveals, :region], region: [parent: [parent: [:parent]]]]
]
end

defp submission_preloads do
[answer: [:reveals], run: [:participations, specification: [answers: [:reveals]], submissions: [answer: [:reveals]]]]
[
answer: [:reveals, :region],
run: [:participations, specification: [answers: [:reveals, :region]], submissions: [answer: [:reveals, :region]]]
]
end

def get_participation!(id),
Expand Down
4 changes: 3 additions & 1 deletion registrations/lib/registrations/waydowntown/answer.ex
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ defmodule Registrations.Waydowntown.Answer do
field(:order, :integer)

belongs_to(:specification, Registrations.Waydowntown.Specification, type: :binary_id)
belongs_to(:region, Registrations.Waydowntown.Region, type: :binary_id)

has_many(:reveals, Registrations.Waydowntown.Reveal, on_delete: :delete_all)

Expand All @@ -23,8 +24,9 @@ defmodule Registrations.Waydowntown.Answer do
@doc false
def changeset(answer, attrs) do
answer
|> cast(attrs, [:answer, :order, :specification_id])
|> cast(attrs, [:answer, :order, :specification_id, :region_id])
|> validate_required([:answer, :order, :specification_id])
|> assoc_constraint(:specification)
|> assoc_constraint(:region)
end
end
8 changes: 6 additions & 2 deletions registrations/lib/registrations_web/views/answer_view.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ defmodule RegistrationsWeb.AnswerView do
def get_field(field, answer, conn) do
cond do
field == :hint ->
if Enum.find(answer.reveals, fn reveal -> reveal.user_id == conn.assigns.current_user.id end) do
if Ecto.assoc_loaded?(answer.reveals) and
Enum.find(answer.reveals, fn reveal -> reveal.user_id == conn.assigns.current_user.id end) do
Map.fetch!(answer, field)
end

Expand All @@ -21,6 +22,9 @@ defmodule RegistrationsWeb.AnswerView do
end

def relationships do
[specification: {RegistrationsWeb.SpecificationView, :include}]
[
specification: {RegistrationsWeb.SpecificationView, :include},
region: {RegistrationsWeb.RegionView, :include}
]
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ defmodule RegistrationsWeb.Owner.AnswerView do
end

def relationships do
[specification: {RegistrationsWeb.Owner.SpecificationView, :include}]
[
specification: {RegistrationsWeb.Owner.SpecificationView, :include},
region: {RegistrationsWeb.RegionView, :include}
]
end
end
15 changes: 15 additions & 0 deletions registrations/priv/concepts.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,27 @@ string_collector:
name: "String Collector"
marker: list_checks
instructions: "enter the strings of text described until you’ve found them all. caps are ignored."
normalise_submissions: true

fill_in_the_blank:
name: "Fill in the Blank"
marker: rectangle_ellipsis
instructions: "submit the correct answer to fill in the blank."

payphone_collector:
name: "Payphone Collector"
marker: phone
instructions: "submit the phone numbers from payphones."
long_running: true
normalise_submissions: true

elevator_collector:
name: "Elevator Collector"
marker: arrow_up_down
instructions: "submit the serial numbers from elevator permits."
long_running: true
normalise_submissions: true

orientation_memory:
name: "Orientation memory"
marker: ratio
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
defmodule Registrations.Repo.Migrations.AddWaydowntownAnswerRegion do
@moduledoc false
use Ecto.Migration

def change do
alter table(:answers, prefix: "waydowntown") do
add(:region_id, references(:regions, type: :uuid, on_delete: :nilify_all))
end
end
end
9 changes: 6 additions & 3 deletions registrations/test/models/specification_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,16 @@ defmodule Registrations.Waydowntown.SpecificationTest do
end

test "validates concept" do
valid_concept = "bluetooth_collector"
valid_concepts = ["bluetooth_collector", "payphone_collector", "elevator_collector"]
invalid_concept = "invalid_concept"

valid_changeset = Specification.changeset(%Specification{}, %{concept: valid_concept, task_description: "test"})
Enum.each(valid_concepts, fn concept ->
valid_changeset = Specification.changeset(%Specification{}, %{concept: concept, task_description: "test"})
assert valid_changeset.valid?
end)

invalid_changeset = Specification.changeset(%Specification{}, %{concept: invalid_concept, task_description: "test"})

assert valid_changeset.valid?
refute invalid_changeset.valid?
assert "must be a known concept" in errors_on(invalid_changeset).concept
end
Expand Down
Loading
Loading