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 Apr 22, 2024
2 parents be8df0d + 67ec9be commit 5860cfb
Show file tree
Hide file tree
Showing 26 changed files with 929 additions and 283 deletions.
4 changes: 4 additions & 0 deletions assets/css/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -140,3 +140,7 @@ https://tailwindcss.com/docs/utility-first
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
}

.search-result em {
@apply not-italic bg-yellow-50;
}
2 changes: 1 addition & 1 deletion assets/src/hooks/scroller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export const Scroller = {
// It is triggered from the backend as follows:
//
// def handle_event(..., socket) do
// {:no_reply, push_event(socket, "scroll-y-to-target", %{id: "element-id", offset: 50, scroll: true, scroll_delay: 500})
// {:no_reply, push_event(socket, "scroll-y-to-target", %{id: "element-id", offset: 50, scroll: true, scroll_delay: 500})}
// end
//
// Expects the id of the element to scroll to, an optional offset
Expand Down
22 changes: 7 additions & 15 deletions lib/oli/analytics/by_activity.ex
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
defmodule Oli.Analytics.ByActivity do
import Ecto.Query, warn: false
alias Oli.Delivery.Sections.SectionResource
alias Oli.Publishing.DeliveryResolver
alias Oli.Repo
alias Oli.Resources.ResourceType
alias Oli.Analytics.Common
alias Oli.Publishing

Expand All @@ -11,17 +12,15 @@ defmodule Oli.Analytics.ByActivity do
def query_against_project_slug(project_slug, filtered_sections) do
project_slug
|> get_base_query(filtered_sections)
|> get_query_with_join_filter(filtered_sections)
|> Repo.all()
end

defp get_base_query(project_slug, filtered_sections) do
subquery =
if filtered_sections != [] do
Publishing.query_unpublished_revisions_by_type_and_section(
project_slug,
"activity",
filtered_sections
DeliveryResolver.revisions_filter_by_section_ids(
filtered_sections,
ResourceType.id_for_activity()
)
else
Publishing.query_unpublished_revisions_by_type(
Expand All @@ -40,14 +39,7 @@ defmodule Oli.Analytics.ByActivity do
number_of_attempts: analytics.number_of_attempts,
relative_difficulty: analytics.relative_difficulty
},
preload: [:resource_type]
end

defp get_query_with_join_filter(query, filter_list) do
from activity in query,
join: resource in assoc(activity, :resource),
left_join: section_resource in SectionResource,
on: resource.id == section_resource.resource_id,
where: section_resource.section_id in ^filter_list
preload: [:resource_type],
distinct: [activity]
end
end
20 changes: 5 additions & 15 deletions lib/oli/analytics/by_objective.ex
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
defmodule Oli.Analytics.ByObjective do
import Ecto.Query, warn: false
alias Oli.Publishing.DeliveryResolver
alias Oli.Delivery.Snapshots.Snapshot
alias Oli.Delivery.Sections.SectionResource

alias Oli.Resources.ResourceType
alias Oli.Repo
alias Oli.Analytics.Common
alias Oli.Publishing
Expand All @@ -14,17 +14,15 @@ defmodule Oli.Analytics.ByObjective do
def query_against_project_slug(project_slug, filtered_sections) do
project_slug
|> get_base_query(get_activity_objectives(project_slug), filtered_sections)
|> get_query_with_join_filter(filtered_sections)
|> Repo.all()
end

defp get_base_query(project_slug, activity_objectives, filtered_sections) do
subquery =
if filtered_sections != [] do
Publishing.query_unpublished_revisions_by_type_and_section(
project_slug,
"objective",
filtered_sections
DeliveryResolver.revisions_filter_by_section_ids(
filtered_sections,
ResourceType.id_for_objective()
)
else
Publishing.query_unpublished_revisions_by_type(
Expand All @@ -50,14 +48,6 @@ defmodule Oli.Analytics.ByObjective do
)
end

defp get_query_with_join_filter(query, filter) do
from objective in query,
join: resource in assoc(objective, :resource),
left_join: section_resource in SectionResource,
on: resource.id == section_resource.resource_id,
where: section_resource.section_id in ^filter
end

defp get_activity_objectives(project_slug) do
from(project in Project,
where: project.slug == ^project_slug,
Expand Down
29 changes: 10 additions & 19 deletions lib/oli/analytics/by_page.ex
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
defmodule Oli.Analytics.ByPage do
import Ecto.Query, warn: false
alias Oli.Delivery.Sections.SectionResource
alias Oli.Publishing.DeliveryResolver
alias Oli.Delivery.Snapshots.Snapshot
alias Oli.Repo
alias Oli.Analytics.Common
alias Oli.Publishing
alias Oli.Resources.ResourceType
alias Oli.Authoring.Course.Project

def query_against_project_slug(project_slug, []),
Expand All @@ -15,25 +16,15 @@ defmodule Oli.Analytics.ByPage do
def query_against_project_slug(project_slug, filtered_sections) do
project_slug
|> get_base_query(get_activity_pages(project_slug), filtered_sections)
|> get_query_with_join_filter(filtered_sections)
|> Repo.all()
end

defp get_query_with_join_filter(query, filter_list) do
from page in query,
join: resource in assoc(page, :resource),
left_join: section_resource in SectionResource,
on: resource.id == section_resource.resource_id,
where: section_resource.section_id in ^filter_list
end

defp get_base_query(project_slug, activity_pages, filtered_sections) do
subquery =
if filtered_sections != [] do
Publishing.query_unpublished_revisions_by_type_and_section(
project_slug,
"page",
filtered_sections
DeliveryResolver.revisions_filter_by_section_ids(
filtered_sections,
ResourceType.id_for_page()
)
else
Publishing.query_unpublished_revisions_by_type(
Expand All @@ -44,10 +35,9 @@ defmodule Oli.Analytics.ByPage do

subquery_activity =
if filtered_sections != [] do
Publishing.query_unpublished_revisions_by_type_and_section(
project_slug,
"activity",
filtered_sections
DeliveryResolver.revisions_filter_by_section_ids(
filtered_sections,
ResourceType.id_for_page()
)
else
Publishing.query_unpublished_revisions_by_type(
Expand All @@ -72,7 +62,8 @@ defmodule Oli.Analytics.ByPage do
number_of_attempts: analytics.number_of_attempts,
relative_difficulty: analytics.relative_difficulty
},
preload: [:resource_type]
preload: [:resource_type],
distinct: [activity]
)
end

Expand Down
18 changes: 18 additions & 0 deletions lib/oli/publishing/delivery_resolver.ex
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,24 @@ defmodule Oli.Publishing.DeliveryResolver do
|> emit([:oli, :resolvers, :delivery], :duration)
end

def revisions_filter_by_section_ids(section_ids, resource_type_id) do
from(sr in SectionResource,
join: s in Section,
on: s.id == sr.section_id,
where: s.id in ^section_ids,
join: spp in SectionsProjectsPublications,
on: s.id == spp.section_id,
where: sr.project_id == spp.project_id,
join: pr in PublishedResource,
on: pr.publication_id == spp.publication_id,
where: pr.resource_id == sr.resource_id,
join: rev in Revision,
on: rev.id == pr.revision_id,
where: rev.resource_type_id == ^resource_type_id and rev.deleted == false,
select: rev
)
end

@impl Resolver
def revisions_of_type(section_slug, resource_type_id) do
fn ->
Expand Down
133 changes: 129 additions & 4 deletions lib/oli/resources/collaboration.ex
Original file line number Diff line number Diff line change
Expand Up @@ -1118,10 +1118,15 @@ defmodule Oli.Resources.Collaboration do
point_block_id \\ nil
) do
filter_by_point_block_id =
if is_nil(point_block_id) do
dynamic([p], is_nil(p.annotated_block_id))
else
dynamic([p], p.annotated_block_id == ^point_block_id)
case point_block_id do
nil ->
true

:page ->
dynamic([p], is_nil(p.annotated_block_id))

point_block_id ->
dynamic([p], p.annotated_block_id == ^point_block_id)
end

Repo.all(
Expand Down Expand Up @@ -1152,6 +1157,126 @@ defmodule Oli.Resources.Collaboration do
|> summarize_reactions(user_id)
end

@doc """
Returns the list of posts that a user can see which match a given search term.
## Examples
iex> search_posts_for_user_in_point_block(1, 1, 1, :private, "1", "search term"))
[%Post{status: :archived}, ...]
iex> search_posts_for_user_in_point_block(2, 2, 2, :private, "2", "search term")
[]
"""
def search_posts_for_user_in_point_block(
section_id,
resource_id,
user_id,
visibility,
point_block_id,
search_term
) do
filter_by_point_block_id =
case point_block_id do
nil ->
true

:page ->
dynamic([p], is_nil(p.annotated_block_id))

point_block_id ->
dynamic([p], p.annotated_block_id == ^point_block_id)
end

Repo.all(
from(
post in Post,
left_join: replies in subquery(replies_subquery()),
on: replies.thread_root_id == post.id,
left_join: read_replies in subquery(read_replies_subquery(user_id)),
on: read_replies.thread_root_id == post.id,
left_join: parent_post in assoc(post, :parent_post),
left_join: reactions in assoc(post, :reactions),
left_join: user in assoc(post, :user),
where:
post.section_id == ^section_id and post.resource_id == ^resource_id and
(post.status in [:approved, :archived] or
(post.status == :submitted and post.user_id == ^user_id)),
where: ^filter_by_point_block_id,
where:
fragment(
"to_tsvector('english', ?) @@ websearch_to_tsquery('english', ?)",
post.content,
^search_term
),
where: post.visibility == ^visibility,
order_by: [desc: :inserted_at],
preload: [
user: user,
reactions: reactions,
parent_post: parent_post
],
select: %{
post
| replies_count: coalesce(replies.count, 0),
read_replies_count: coalesce(read_replies.count, 0),
headline:
fragment(
"""
ts_headline(
'english',
?,
websearch_to_tsquery(?),
'StartSel=<em>,StopSel=</em>,MinWords=25,MaxWords=75'
)
""",
post.content,
^search_term
)
}
)
)
|> summarize_reactions(user_id)
|> group_by_parent_post()
end

defp group_by_parent_post(posts) do
by_parent_id = Enum.group_by(posts, &Map.get(&1, :parent_post_id))

# we want to preserve the order of the posts returned by the query
# so we need to reduce over the list of posts and place each post in the correct
# parent post's replies list
{results, _} =
posts
|> Enum.reduce({[], by_parent_id}, fn post, {acc, by_parent_id} ->
parent_post_id = post.parent_post_id

case parent_post_id do
nil ->
# this is a top-level post
{[post | acc], by_parent_id}

_ ->
# this is a reply post, so place parent post with it's replies we already groups
# in the list if it's not there yet
case by_parent_id[parent_post_id] do
nil ->
# parent post is already in the list, so skip it
{acc, by_parent_id}

replies ->
# parent post is not in the list yet, so add it and drop it from the by_parent_id map
# to track which parent posts we already processed
{[Map.put(post.parent_post, :replies, replies) | acc],
Map.delete(by_parent_id, parent_post_id)}
end
end
end)

results
|> Enum.reverse()
end

defp summarize_reactions(posts, current_user_id) do
Enum.map(posts, fn post ->
%{
Expand Down
2 changes: 2 additions & 0 deletions lib/oli/resources/collaboration/post.ex
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ defmodule Oli.Resources.Collaboration.Post do
has_many :reactions, Oli.Resources.Collaboration.UserReactionPost

field :reaction_summaries, :map, virtual: true
field :headline, :string, virtual: true
field :replies, :any, virtual: true

timestamps(type: :utc_datetime)
end
Expand Down
8 changes: 6 additions & 2 deletions lib/oli_web/components/delivery/instructor_dashboard.ex
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ defmodule OliWeb.Components.Delivery.InstructorDashboard do
<div class="relative flex-1 flex flex-col pt-4 pb-[60px]">
<%= render_slot(@inner_block) %>
<%= Phoenix.View.render(OliWeb.LayoutView, "_delivery_footer.html", assigns) %>
<OliWeb.Components.Footer.delivery_footer license={
Map.get(assigns, :has_license) && assigns[:license]
} />
</div>
</div>
"""
Expand Down Expand Up @@ -273,7 +275,9 @@ defmodule OliWeb.Components.Delivery.InstructorDashboard do

def footer(assigns) do
~H"""
<%= Phoenix.View.render(OliWeb.LayoutView, "_delivery_footer.html", assigns) %>
<OliWeb.Components.Footer.delivery_footer license={
Map.get(assigns, :has_license) && assigns[:license]
} />
"""
end
end
Loading

0 comments on commit 5860cfb

Please sign in to comment.