Skip to content

Commit

Permalink
Merge branch 'Simon-Initiative:master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
dtiwarATS authored Feb 19, 2024
2 parents b181bb2 + 8edb8d9 commit d2c6696
Show file tree
Hide file tree
Showing 7 changed files with 199 additions and 87 deletions.
16 changes: 14 additions & 2 deletions assets/src/apps/scheduler/scheduler-slice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,22 @@ const buildHierarchyItems = (
items: HierarchyItemSrc[],
preferredSchedulingTime: TimeParts,
): HierarchyItem[] => {
const parseDate = (str: string, scheduleType: string) => {
if (!str) return null;

if (scheduleType === 'due_by' && str.length > 10) {
// For due-by items, we need to take the time into account to get the correct date
const tempDate = new Date(str);
return new DateWithoutTime(tempDate.getFullYear(), tempDate.getMonth(), tempDate.getDate());
}

return str ? new DateWithoutTime(str) : null;
};

return items.map((item) => ({
...item,
startDate: item.start_date ? new DateWithoutTime(item.start_date) : null,
endDate: item.end_date ? new DateWithoutTime(item.end_date) : null,
startDate: parseDate(item.start_date, item.scheduling_type),
endDate: parseDate(item.end_date, item.scheduling_type),
endDateTime: toDateTime(item.end_date, preferredSchedulingTime),
startDateTime: toDateTime(item.start_date, preferredSchedulingTime),
}));
Expand Down
4 changes: 2 additions & 2 deletions assets/styles/adaptive/flowchart/text-flow.scss
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
/*
Flowchart mode text flow styles
Flowchart mode text flow styles
The authoring component is in a shadow-dom, so these have to mirror that, and not customize it.
*/

janus-text-flow[type='janus-text-flow'] {
div[data-janus-type='janus-text-flow'] {
font-size: 16px;
font-size: 13px;
}

h1 {
Expand Down
3 changes: 1 addition & 2 deletions config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,7 @@ config :oli, Oban,
analytics_export: 3,
datashop_export: 3,
objectives: 3
],
testing: :manual
]

config :ex_money,
auto_start_exchange_rate_service: false,
Expand Down
3 changes: 2 additions & 1 deletion config/test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ config :oli, Oli.Repo,

config :oli, Oban,
plugins: false,
queues: false
queues: false,
testing: :manual

# Configure reCAPTCHA
config :oli, :recaptcha,
Expand Down
158 changes: 78 additions & 80 deletions lib/oli/delivery/sections.ex
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ defmodule Oli.Delivery.Sections do
alias OliWeb.Common.FormatDateTime
alias Oli.Delivery.PreviousNextIndex
alias Ecto.Multi
alias Oli.Delivery.Gating.GatingCondition
alias Oli.Delivery.Attempts.Core.ResourceAccess
alias Oli.Delivery.Metrics
alias Oli.Delivery.Paywall
Expand Down Expand Up @@ -3054,95 +3053,94 @@ defmodule Oli.Delivery.Sections do
end
end

@scheduling_types Ecto.ParameterizedType.init(Ecto.Enum,
values: [:due_by, :read_by, :inclass_activity, :schedule]
)
@doc """
Returns the resources scheduled dates for a given student.
A Student exception takes precedence over all other end dates.
Hard sceduled dates for a specific student take precedence over "global" hard scheduled dates.
Global hard scheduled dates take precedence over soft scheduled dates.
"""
@spec get_resources_scheduled_dates_for_student(String.t(), integer()) ::
list(%{
integer => %{
end_date: DateTime.t() | nil,
scheduled_type: :due_by | :read_by | :inclass_activity | :schedule
}
})
def get_resources_scheduled_dates_for_student(section_slug, student_id) do
get_soft_scheduled_dates(section_slug)
|> Map.merge(get_hard_scheduled_dates(section_slug))
|> Map.merge(get_hard_scheduled_dates_for_student(section_slug, student_id))
|> Map.merge(get_student_exception_end_dates(section_slug, student_id))
end

def get_soft_scheduled_dates(section_slug) do
query =
from([sr, _s, _spp, _pr, _rev] in DeliveryResolver.section_resource_revisions(section_slug),
select: {
sr.resource_id,
%{end_date: sr.end_date, scheduled_type: sr.scheduling_type}
}
)

Repo.all(query)
|> Enum.into(%{})
end

def get_hard_scheduled_dates(section_slug) do
query =
from([_sr, s, _spp, _pr, _rev] in DeliveryResolver.section_resource_revisions(section_slug),
join: gc in GatingCondition,
on: gc.section_id == s.id,
where: gc.type == :schedule and is_nil(gc.user_id),
select: {
gc.resource_id,
%{
end_date:
fragment(
"CASE WHEN ? = 'null' THEN NULL ELSE cast(cast(? as text) as date) END",
gc.data["end_datetime"],
gc.data["end_datetime"]
),
scheduled_type: gc.type
}
}
)

Repo.all(query)
|> Enum.into(%{})
end

def get_hard_scheduled_dates_for_student(section_slug, student_id) do
query =
from([_sr, s, _spp, _pr, _rev] in DeliveryResolver.section_resource_revisions(section_slug),
join: gc in GatingCondition,
on: gc.section_id == s.id,
where: gc.type == :schedule and gc.user_id == ^student_id,
select: {
gc.resource_id,
%{
end_date:
fragment(
"CASE WHEN ? = 'null' THEN NULL ELSE cast(cast(? as text) as date) END",
gc.data["end_datetime"],
gc.data["end_datetime"]
),
scheduled_type: gc.type
}
}
)

Repo.all(query)
|> Enum.into(%{})
end

def get_student_exception_end_dates(section_slug, student_id) do
from([sr, s, _spp, _pr, _rev] in DeliveryResolver.section_resource_revisions(section_slug),
join: se in Oli.Delivery.Settings.StudentException,
on: se.section_id == s.id and se.resource_id == sr.resource_id,
where: se.user_id == ^student_id and not is_nil(se.end_date),
select: {
sr.resource_id,
%{
end_date: se.end_date,
scheduled_type: sr.scheduling_type
}
from(sr in Oli.Delivery.Sections.SectionResource,
join: s in Oli.Delivery.Sections.Section,
on: sr.section_id == s.id and s.slug == ^section_slug,
left_join: se in Oli.Delivery.Settings.StudentException,
on:
se.section_id == sr.section_id and se.resource_id == sr.resource_id and
se.user_id == ^student_id and not is_nil(se.end_date),
left_join: gc1 in Oli.Delivery.Gating.GatingCondition,
on:
gc1.section_id == sr.section_id and gc1.resource_id == sr.resource_id and
gc1.type == :schedule and gc1.user_id == ^student_id,
left_join: gc2 in Oli.Delivery.Gating.GatingCondition,
on:
gc2.section_id == sr.section_id and gc2.resource_id == sr.resource_id and
gc2.type == :schedule and is_nil(gc2.user_id),
select: %{
resource_id:
fragment(
"coalesce(?, ?, ?, ?)",
se.resource_id,
gc1.resource_id,
gc2.resource_id,
sr.resource_id
),
end_date:
fragment(
"""
COALESCE(
?,
CASE WHEN ? = 'null' THEN NULL ELSE CAST(CAST(? AS text) AS timestamp) END,
CASE WHEN ? = 'null' THEN NULL ELSE CAST(CAST(? AS text) AS timestamp) END,
?
)
""",
se.end_date,
gc1.data["end_datetime"],
gc1.data["end_datetime"],
gc2.data["end_datetime"],
gc2.data["end_datetime"],
sr.end_date
)
|> type(:utc_datetime),
scheduled_type:
fragment(
"""
CASE WHEN ? IS NULL THEN
CASE WHEN ? IS NULL THEN
CASE WHEN ? IS NULL THEN ? ELSE ? END
ELSE ? END
ELSE ? END
""",
se.resource_id,
gc1.resource_id,
gc2.resource_id,
sr.scheduling_type,
gc2.type,
gc1.type,
sr.scheduling_type
)
|> type(^@scheduling_types)
}
)
|> Repo.all()
|> Enum.into(%{})
|> Enum.reduce(%{}, fn %{
end_date: end_date,
resource_id: resource_id,
scheduled_type: scheduled_type
},
acc ->
Map.put(acc, resource_id, %{end_date: end_date, scheduled_type: scheduled_type})
end)
end

defp get_pages(section_slug) do
Expand Down
25 changes: 25 additions & 0 deletions lib/oli_web/controllers/api/attempt_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -664,6 +664,31 @@ defmodule OliWeb.Api.AttemptController do
end
end

def new_activity(
conn,
%{
"section_slug" => section_slug,
"activity_attempt_guid" => activity_attempt_guid
} = params
) do
seed_state_from_previous = Map.get(params, "seedResponsesWithPrevious", false)
datashop_session_id = Plug.Conn.get_session(conn, :datashop_session_id)

case Activity.reset_activity(
section_slug,
activity_attempt_guid,
datashop_session_id,
seed_state_from_previous
) do
{:ok, {attempt_state, model}} ->
json(conn, %{"type" => "success", "attemptState" => attempt_state, "model" => model})

{:error, e} ->
{_, msg} = Oli.Utils.log_error("Could not reset activity", e)
error(conn, 500, msg)
end
end

def file_upload(conn, %{
"section_slug" => section_slug,
"activity_attempt_guid" => activity_guid,
Expand Down
77 changes: 77 additions & 0 deletions test/oli/sections_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,83 @@ defmodule Oli.SectionsTest do
alias Oli.Delivery.Snapshots.Snapshot
alias Oli.Delivery.Snapshots
alias Oli.Delivery.Transfer
alias Oli.Publishing.DeliveryResolver

describe "get_resources_scheduled_dates_for_student/2" do
# SE: Student exception
# GCwS: Hard scheduled dates for student
# GCnS: Hard scheduled dates
# SR: Soft scheduled_dates
# Arrow pointing down indicates the dominant datetime per resource
# res_1 res_2 res_3 res_4 res_5 res_6 res_7
# ↓ ↓
# SE ↓ 04/08 ↓ 04/21
# GCwS ↓ 03/17 03/18 ↓ 03/20
# GCnS ↓ 02/16 02/17 03/18 02/19
# SR 01/15 01/16 01/17 01/18 01/19 01/20 01/21
test "returns correct datetime/type order" do
sec = insert(:section, slug: "section_slug")

%{project: proj} =
insert(:publication, project: insert(:project, authors: [insert(:author)]))

[res_1, res_2, res_3, res_4, res_5, res_6, res_7] = res_list = insert_list(7, :resource)

res_ids_end_dates = [
{res_1.id, ~U[2000-01-15 12:00:00Z]},
{res_2.id, ~U[2000-01-16 12:00:00Z]},
{res_3.id, ~U[2000-01-17 12:00:00Z]},
{res_4.id, ~U[2000-01-18 12:00:00Z]},
{res_5.id, ~U[2000-01-19 12:00:00Z]},
{res_6.id, ~U[2000-01-20 12:00:00Z]},
{res_7.id, ~U[2000-01-21 12:00:00Z]}
]

Enum.each(res_ids_end_dates, fn {res_id, dt} ->
insert(:section_resource, section: sec, resource_id: res_id, project: proj, end_date: dt)
end)

dt = %{start_datetime: ~U[2000-01-01 12:00:00Z], end_datetime: ~U[2000-01-01 12:00:00Z]}
data = Map.put(dt, :end_datetime, ~U[2000-02-16 12:00:00Z])
_gc = insert(:gating_condition, user: nil, section: sec, resource: res_2, data: data)
data = Map.put(dt, :end_datetime, ~U[2000-02-17 12:00:00Z])
_gc = insert(:gating_condition, user: nil, section: sec, resource: res_3, data: data)
data = Map.put(dt, :end_datetime, ~U[2000-02-18 12:00:00Z])
_gc = insert(:gating_condition, user: nil, section: sec, resource: res_4, data: data)

user = insert(:user)
data = Map.put(dt, :end_datetime, ~U[2000-03-17 12:00:00Z])
_gc = insert(:gating_condition, user: user, section: sec, resource: res_3, data: data)
data = Map.put(dt, :end_datetime, ~U[2000-03-18 12:00:00Z])
_gc = insert(:gating_condition, user: user, section: sec, resource: res_4, data: data)

se_dt = ~U[2000-04-18 12:00:00Z]
_se = insert(:student_exception, user: user, section: sec, resource: res_4, end_date: se_dt)

data = Map.put(dt, :end_datetime, ~U[2000-02-19 12:00:00Z])
_gc = insert(:gating_condition, user: nil, section: sec, resource: res_5, data: data)
data = Map.put(dt, :end_datetime, ~U[2000-03-20 12:00:00Z])
_gc = insert(:gating_condition, user: user, section: sec, resource: res_6, data: data)
se_dt = ~U[2000-04-21 12:00:00Z]

_se =
insert(:student_exception, user: user, section: sec, resource: res_7, end_date: se_dt)

[res_1_id, res_2_id, res_3_id, res_4_id, res_5_id, res_6_id, res_7_id] =
Enum.map(res_list, & &1.id)

assert %{
^res_1_id => %{end_date: ~U[2000-01-15 12:00:00Z], scheduled_type: :read_by},
^res_2_id => %{end_date: ~U[2000-02-16 12:00:00Z], scheduled_type: :schedule},
^res_3_id => %{end_date: ~U[2000-03-17 12:00:00Z], scheduled_type: :schedule},
^res_4_id => %{end_date: ~U[2000-04-18 12:00:00Z], scheduled_type: :read_by},
^res_5_id => %{end_date: ~U[2000-02-19 12:00:00Z], scheduled_type: :schedule},
^res_6_id => %{end_date: ~U[2000-03-20 12:00:00Z], scheduled_type: :schedule},
^res_7_id => %{end_date: ~U[2000-04-21 12:00:00Z], scheduled_type: :read_by}
} =
Sections.get_resources_scheduled_dates_for_student(sec.slug, user.id)
end
end

describe "enrollments" do
@valid_attrs %{
Expand Down

0 comments on commit d2c6696

Please sign in to comment.