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 committed Sep 16, 2024
2 parents 2c8baab + 9fa6f74 commit b34717f
Show file tree
Hide file tree
Showing 52 changed files with 2,383 additions and 531 deletions.
2 changes: 2 additions & 0 deletions assets/src/apps/bibliography/BibEntryView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ export const BibEntryView: React.FC<BibEntryViewProps> = (props: BibEntryViewPro
format: 'html',
template: 'apa',
lang: 'en-US',
// include any note, used for URL in legacy bib entries
append: (entry: any) => ` ${entry.note}`,
});
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ export const CitationEditor = (props: ExistingCiteEditorProps) => {
format: 'html',
template: 'apa',
lang: 'en-US',
// include any note, used for URL in legacy bib entries
append: (entry: any) => ` ${entry.note}`,
});
};

Expand Down
7 changes: 7 additions & 0 deletions assets/styles/adaptive/activities/dropdown.scss
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,10 @@ janus-input-number .unitsLabel {
margin-left: 10px;
margin-top: 5px;
}

//As per the requirment, dropdown / select needs to be in light mode even if the current theme is Dark.
// This can be customized from any external CSS file.
.dark select {
background-color: #fff;
color: #333333;
}
7 changes: 7 additions & 0 deletions assets/styles/adaptive/activities/popup.scss
Original file line number Diff line number Diff line change
Expand Up @@ -150,3 +150,10 @@ janus-popup:has(input) {
}
}
}

.dark janus-popup:has(input) {
input[src*='data']:focus,
input[src*='data']:active {
background-color: rgba(0, 0, 0, 0);
}
}
9 changes: 9 additions & 0 deletions assets/styles/adaptive/activities/text-input.scss
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,12 @@ janus-input-number .unitsLabel {
margin-left: 10px;
margin-top: 10px;
}

//As per the requirment, text & number input needs to be in light mode even if the current theme is Dark.
// This can be customized from any external CSS file.
.dark input[type='datetime-local'],
.dark input[type='number'],
.dark input[type='text'] {
background-color: #fff;
color: #333333;
}
6 changes: 6 additions & 0 deletions assets/styles/preview/preview-tools.scss
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,9 @@
.list-group {
margin-left: 1rem !important;
}
.list-group {
margin: 0px !important;
}
button {
font-size: 0.9rem;
}
Expand Down Expand Up @@ -214,3 +217,6 @@
width: 1px;
}
}
.dark .inspector .stateKey {
color: white !important;
}
294 changes: 294 additions & 0 deletions lib/oli/analytics/summary/browse_insights.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,294 @@
defmodule Oli.Analytics.Summary.BrowseInsights do
alias Oli.Publishing.Publications.Publication
alias Oli.Publishing.PublishedResource
alias Oli.Analytics.Summary.BrowseInsightsOptions
alias Oli.Analytics.Summary.ResourceSummary
alias Oli.Resources.Revision
alias Oli.Repo.{Paging, Sorting}
import Ecto.Query, warn: false
alias Oli.Repo

defp get_relative_difficulty_parameters() do
alpha = Application.get_env(:oli, :relative_difficulty_alpha, 0.5)
beta = Application.get_env(:oli, :relative_difficulty_beta, 0.3)
gamma = Application.get_env(:oli, :relative_difficulty_gamma, 0.2)

{alpha, beta, gamma}
end

def browse_insights(
%Paging{limit: limit, offset: offset},
%Sorting{} = sorting,
%BrowseInsightsOptions{project_id: project_id, section_ids: section_ids} = options
) do
where_by = build_where_by(options)
total_count = get_total_count(project_id, section_ids, where_by)

# Now build the main query with limit, offset, and aggregations
query =
ResourceSummary
|> join(:left, [s], pub in Publication, on: pub.project_id == ^project_id)
|> join(:left, [s, pub], pr in PublishedResource, on: pr.publication_id == pub.id)
|> join(:left, [s, pub, pr], rev in Revision, on: rev.id == pr.revision_id)
|> where(^where_by)
|> add_select(total_count, options)
|> add_order_by(sorting, options)
|> limit(^limit)
|> offset(^offset)

Repo.all(query)
end

defp build_where_by(%BrowseInsightsOptions{
project_id: project_id,
resource_type_id: resource_type_id,
section_ids: section_ids
}) do
case section_ids do
[] ->
dynamic(
[s, pub, pr, _],
s.project_id == ^project_id and
s.resource_id == pr.resource_id and
is_nil(pub.published) and
s.resource_type_id == ^resource_type_id and
s.section_id == -1 and
s.user_id == -1 and
s.publication_id == -1
)

section_ids ->
dynamic(
[s, pub, pr, _],
s.resource_id == pr.resource_id and
is_nil(pub.published) and
s.resource_type_id == ^resource_type_id and
s.section_id in ^section_ids and
s.user_id == -1 and
s.publication_id == -1
)
end
end

defp add_select(query, total_count, %BrowseInsightsOptions{section_ids: section_ids}) do
{alpha, beta, gamma} = get_relative_difficulty_parameters()

case section_ids do
[] ->
query
|> select([s, pub, pr, rev], %{
id: s.id,
total_count: fragment("?::int", ^total_count),
title: rev.title,
resource_id: s.resource_id,
slug: rev.slug,
part_id: s.part_id,
pub_id: pub.id,
activity_type_id: rev.activity_type_id,
pr_rev: pr.revision_id,
pr_resource: pr.resource_id,
num_attempts: s.num_attempts,
num_first_attempts: s.num_first_attempts,
eventually_correct: fragment("?::float8 / ?::float8", s.num_correct, s.num_attempts),
first_attempt_correct:
fragment("?::float8 / ?::float8", s.num_first_attempts_correct, s.num_first_attempts),
relative_difficulty:
fragment(
"?::float8 * (1.0 - ?::float8) + ?::float8 * (1.0 - ?::float8) + ?::float8 * ?::float8",
^alpha,
s.num_first_attempts_correct / s.num_first_attempts,
^beta,
s.num_correct / s.num_attempts,
^gamma,
s.num_hints
)
})

_section_ids ->
query
|> group_by([s, _, _, rev], [
s.resource_id,
s.part_id,
rev.title,
rev.slug,
rev.activity_type_id
])
|> select([s, _, _, rev], %{
# select id as a random GUID
id: fragment("gen_random_uuid()::text"),
total_count: fragment("?::int", ^total_count),
resource_id: s.resource_id,
title: rev.title,
slug: rev.slug,
part_id: s.part_id,
activity_type_id: rev.activity_type_id,
num_attempts: sum(s.num_attempts),
num_first_attempts: sum(s.num_first_attempts),
eventually_correct:
fragment("?::float8 / ?::float8", sum(s.num_correct), sum(s.num_attempts)),
first_attempt_correct:
fragment(
"?::float8 / ?::float8",
sum(s.num_first_attempts_correct),
sum(s.num_first_attempts)
),
relative_difficulty:
fragment(
"?::float8 * (1.0 - (?::float8)) + ?::float8 * (1.0 - (?::float8)) + ?::float8 * (?::float8)",
^alpha,
sum(s.num_first_attempts_correct) / sum(s.num_first_attempts),
^beta,
sum(s.num_correct) / sum(s.num_attempts),
^gamma,
sum(s.num_hints)
)
})
end
end

defp add_order_by(query, %Sorting{direction: direction, field: field}, %BrowseInsightsOptions{
section_ids: []
}) do
{alpha, beta, gamma} = get_relative_difficulty_parameters()

query =
case field do
:title ->
order_by(query, [_, _, _, rev], {^direction, rev.title})

:part_id ->
order_by(query, [s], {^direction, s.part_id})

:num_attempts ->
order_by(query, [s], {^direction, s.num_attempts})

:num_first_attempts ->
order_by(query, [s], {^direction, s.num_first_attempts})

:eventually_correct ->
order_by(
query,
[s],
{^direction, fragment("?::float8 / ?::float8", s.num_correct, s.num_attempts)}
)

:first_attempt_correct ->
order_by(
query,
[s],
{^direction,
fragment("?::float8 / ?::float8", s.num_first_attempts_correct, s.num_first_attempts)}
)

:relative_difficulty ->
order_by(
query,
[s],
{^direction,
fragment(
"?::float8 * (1.0 - ?::float8) + ?::float8 * (1.0 - ?::float8) + ?::float8 * ?::float8",
^alpha,
s.num_first_attempts_correct / s.num_first_attempts,
^beta,
s.num_correct / s.num_attempts,
^gamma,
s.num_hints
)}
)

_ ->
order_by(query, [_, _, _, rev], {^direction, field(rev, ^field)})
end

# Ensure there is always a stable sort order based on id
order_by(query, [s], s.resource_id)
end

defp add_order_by(query, %Sorting{direction: direction, field: field}, %BrowseInsightsOptions{
section_ids: _
}) do
{alpha, beta, gamma} = get_relative_difficulty_parameters()

query =
case field do
:title ->
order_by(query, [_, _, _, rev], {^direction, rev.title})

:part_id ->
order_by(query, [s], {^direction, s.part_id})

:num_attempts ->
order_by(query, [s], {^direction, sum(s.num_attempts)})

:num_first_attempts ->
order_by(query, [s], {^direction, sum(s.num_first_attempts)})

:eventually_correct ->
order_by(
query,
[s],
{^direction,
fragment("?::float8 / ?::float8", sum(s.num_correct), sum(s.num_attempts))}
)

:first_attempt_correct ->
order_by(
query,
[s],
{^direction,
fragment(
"?::float8 / ?::float8",
sum(s.num_first_attempts_correct),
sum(s.num_first_attempts)
)}
)

:relative_difficulty ->
order_by(
query,
[s],
{^direction,
fragment(
"?::float8 * (1.0 - (?::float8)) + ?::float8 * (1.0 - (?::float8)) + ?::float8 * (?::float8)",
^alpha,
sum(s.num_first_attempts_correct) / sum(s.num_first_attempts),
^beta,
sum(s.num_correct) / sum(s.num_attempts),
^gamma,
sum(s.num_hints)
)}
)

_ ->
order_by(query, [_, _, _, rev], {^direction, field(rev, ^field)})
end

# Ensure there is always a stable sort order based on id
order_by(query, [s], s.resource_id)
end

defp get_total_count(project_id, section_ids, where_by) do
add_group_by = fn query, section_ids ->
case section_ids do
[] -> query
_section_ids -> query |> group_by([s, _, _, _], [s.resource_id, s.part_id])
end
end

# First, we compute the total count separately
total_count_query =
ResourceSummary
|> join(:left, [s], pub in Publication, on: pub.project_id == ^project_id)
|> join(:left, [s, pub], pr in PublishedResource, on: pr.publication_id == pub.id)
|> where(^where_by)
|> add_group_by.(section_ids)
|> select([s, _], fragment("count(*) OVER() as total_count"))
|> limit(1)

Repo.one(total_count_query)
|> case do
nil -> 0
count -> count
end
end
end
23 changes: 23 additions & 0 deletions lib/oli/analytics/summary/browse_insights_options.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
defmodule Oli.Analytics.Summary.BrowseInsightsOptions do
@moduledoc """
Params for browse insights queries.
"""

@enforce_keys [
:project_id,
:section_ids,
:resource_type_id
]

defstruct [
:project_id,
:section_ids,
:resource_type_id
]

@type t() :: %__MODULE__{
project_id: integer(),
section_ids: list(),
resource_type_id: integer()
}
end
2 changes: 1 addition & 1 deletion lib/oli/authoring/editing/resource_editor.ex
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ defmodule Oli.Authoring.Editing.ResourceEditor do
.`{:ok, [%Revision{}]}` when the resources are retrieved
.`{:error, {:not_found}}` if the project is not found
"""
@spec list(String.t(), any(), Integer.t()) ::
@spec list(String.t(), any(), integer()) ::
{:ok, [%Revision{}]} | {:error, {:not_found}}
def list(project_slug, author, resource_type_id) do
with {:ok, project} <- Course.get_project_by_slug(project_slug) |> trap_nil(),
Expand Down
Loading

0 comments on commit b34717f

Please sign in to comment.