Packages
ami
0.2.8
0.3.9
0.3.8
retired
0.3.7
retired
0.3.6
retired
0.3.5
retired
0.3.4
retired
0.3.3
retired
0.3.2
retired
0.3.1
retired
0.3.0
retired
0.2.9
retired
0.2.8
retired
0.2.7
retired
0.2.6
retired
0.2.5
retired
0.2.3
retired
0.2.2
retired
0.2.1
retired
0.2.0
retired
0.1.9
retired
0.1.8
retired
0.1.7
retired
0.1.6
retired
0.1.5
retired
0.1.4
retired
Package containing all AMI Models shared between micro-services.
Retired package: Security issue - retiring
Current section
Files
Jump to
Current section
Files
lib/ami/lesson.ex
defmodule Ami.Lesson do
use Ecto.Schema
import Ecto.{
Query,
Changeset
}
alias Ami.{
Tag,
Repo,
Unit,
Quiz,
Post,
Video,
Score,
Course,
Resource,
UserAnswer,
UnitLesson,
LessonResource,
LessonActivity,
LessonMainResource,
ResourceAttachment
}
schema "lessons" do
@timestamps_opts [type: :naive_datetime_usec]
field(:title, :string)
field(:description, :string)
field(:created_at, :utc_datetime_usec)
field(:updated_at, :utc_datetime_usec)
field(:paid, :boolean)
field(:sequential_tags, :string, virtual: true)
field(:with_activity, :boolean, default: false, virtual: true)
# belongs_to(:unit, Unit)
has_many(:unit_lessons, UnitLesson)
has_many(:units, through: [:unit_lessons, :unit])
has_one(:main_resource, LessonMainResource)
has_one(:main_resource_attachment, through: [:main_resource, :resource_attachment])
has_one(:lesson_activity, LessonActivity)
has_one(:lesson_activity_attachment, through: [:lesson_activity, :resource_attachment])
has_many(:lessons_resources, LessonResource)
has_many(:resource_attachments, through: [:lessons_resources, :resource_attachment])
many_to_many(:tags, Tag, join_through: "lessons_tags", on_replace: :delete)
# In Rails we had a polymorphic association for this which is not implemented in Ecto
# Scope is also not possible in Ecto so check function with_preloaded_videos function for more information.
has_many(:videos, Video, foreign_key: :videoable_id)
# Also a polymorphic association
has_many(:resources, Resource, foreign_key: :resourceable_id)
has_many(:posts, Post)
has_one(:quiz, Quiz)
end
@doc """
Because of better design patterns implemented by Ecto we cannot return lesson.videos to return correct videos.
preload_videos implement a pattern which will return lesson.videos to return only videos with videoable_type == "Lesson" for the given lesson
"""
def preload_videos(struct), do: Video.preload_videos(struct, "Lesson")
@doc """
Pattern to fetch resources as lesson.resources with resourceable_type == "Lesson"
"""
def preload_resources(struct), do: Resource.preload_resources(struct, "Lesson")
@doc """
Check if the lesson is completed or not
"""
def completed?(lesson, enrollment) do
lesson = Repo.preload(lesson, [:quiz])
if lesson.quiz do
if !lesson.quiz.is_final do
find(Score, lesson.quiz.id, enrollment.course_period_id, enrollment.user_id, enrollment.id) != nil
else
find(Score, lesson.quiz.id, enrollment.course_period_id, enrollment.user_id, enrollment.id) != nil && enrollment.assignment_status == "accepted"
end
end
end
# Helper to find User Answers or Score with smiliar arguments
defp find(module, quiz_id, course_period_id, user_id) do
Repo.get_by(module, quiz_id: quiz_id, course_period_id: course_period_id, user_id: user_id)
end
defp find(module, quiz_id, course_period_id, user_id, enrollment_id) do
Repo.get_by(module, quiz_id: quiz_id, course_period_id: course_period_id, user_id: user_id, enrollment_id: enrollment_id)
end
@doc """
Helper checking if a lesson has got an 'Activity' resource
"""
def has_activity?(lesson) do
lesson = Repo.preload(lesson, :lesson_activity)
!is_nil(lesson.lesson_activity)
end
def changeset(%__MODULE__{} = lesson, params \\ %{}) do
lesson
|> cast(params, [:title, :description, :sequential_tags])
|> parse_tags
|> cast_assoc(:tags)
|> cast_assoc(:unit_lessons)
|> cast_assoc(:main_resource)
|> cast_assoc(:lesson_activity)
|> cast_assoc(:lessons_resources)
end
def changeset_with_quiz(%__MODULE__{} = lesson, params \\ %{}) do
lesson
|> cast(params, [:title, :description, :sequential_tags])
|> parse_tags
|> cast_assoc(:tags)
|> cast_assoc(:quiz)
|> cast_assoc(:unit_lessons)
|> cast_assoc(:main_resource)
|> cast_assoc(:lesson_activity)
|> cast_assoc(:lessons_resources)
end
def conditionaly_cast_assoc(changeset, :lesson_activity) do
if is_nil(changeset.params["lesson_activity"]["resource_attachment"]["file"]) do
changeset
else
cast_assoc(changeset, :lesson_activity)
end
end
def parse_tags(changeset) do
if is_nil(changeset.params["sequencial_tags"]) do
changeset
else
stringify_tags = changeset.params["sequencial_tags"]
tags =
String.split(stringify_tags, ", ", trim: true)
|> Enum.map(fn tag -> find_or_build_tag(tag) end)
put_change(changeset, :tags, tags)
end
end
def struct_for_lesson_wizard() do
%__MODULE__{
unit_lessons: [%UnitLesson{unit: %Unit{}}],
lessons_resources: [
%LessonResource{resource_attachment: %ResourceAttachment{}},
%LessonResource{resource_attachment: %ResourceAttachment{}},
%LessonResource{resource_attachment: %ResourceAttachment{}},
%LessonResource{resource_attachment: %ResourceAttachment{}},
%LessonResource{resource_attachment: %ResourceAttachment{}},
%LessonResource{resource_attachment: %ResourceAttachment{}},
%LessonResource{resource_attachment: %ResourceAttachment{}}
]
}
end
def find_or_build_tag(tag_name) do
Tag.find_or_build_tag(tag_name)
end
def search(term) do
course_query = from(c in Course, select: [:title])
results =
Repo.all(
from l in __MODULE__,
left_join: t in assoc(l, :tags),
left_join: ul in assoc(l, :unit_lessons),
left_join: u in assoc(ul, :unit),
left_join: c in assoc(u, :course),
where: ilike(l.title, ^"%#{term}%"),
or_where: ilike(t.name, ^"%#{term}%"),
distinct: l.id,
preload: [
units: {u, [course: ^course_query]}
]
)
|> Enum.map(fn lesson ->
courses = Enum.map(lesson.units, fn unit -> unit.course.title end)
Map.put_new(lesson, :courses, courses)
end)
{:ok, results}
end
end