defmodule Cairnloop.KnowledgeAutomation.ArticleSuggestionEvidence do
use Ecto.Schema
import Ecto.Changeset
@primary_key false
@source_types [:knowledge_base, :resolved_case, :unknown]
@trust_levels [:canonical, :assistive, :unknown]
@max_excerpt_length 500
@max_title_length 200
embedded_schema do
field(:source_type, Ecto.Enum, values: @source_types, default: :unknown)
field(:trust_level, Ecto.Enum, values: @trust_levels, default: :unknown)
field(:title, :string)
field(:excerpt, :string)
field(:citation_target, :map, default: %{})
field(:metadata, :map, default: %{})
field(:match_reasons, {:array, :string}, default: [])
end
def changeset(evidence, attrs) do
evidence
|> cast(attrs, [
:source_type,
:trust_level,
:title,
:excerpt,
:citation_target,
:metadata,
:match_reasons
])
|> validate_required([
:source_type,
:trust_level,
:title,
:excerpt,
:citation_target,
:metadata
])
|> validate_length(:title, max: @max_title_length)
|> validate_length(:excerpt, max: @max_excerpt_length)
|> validate_citation_target()
|> validate_metadata_destination()
end
defp validate_citation_target(changeset) do
validate_change(changeset, :citation_target, fn :citation_target, citation_target ->
cond do
not is_map(citation_target) ->
[citation_target: "must be a map"]
map_size(citation_target) > 5 ->
[citation_target: "must contain at most 5 keys"]
missing_citation_keys?(citation_target) ->
[citation_target: "must include article_id, revision_id, and chunk_index"]
true ->
[]
end
end)
end
defp validate_metadata_destination(changeset) do
validate_change(changeset, :metadata, fn :metadata, metadata ->
destination = metadata_destination(metadata)
cond do
not is_map(metadata) ->
[metadata: "must be a map"]
not is_map(destination) ->
[metadata: "must include metadata.destination"]
map_size(destination) > 4 ->
[metadata: "must contain at most 4 keys"]
true ->
[]
end
end)
end
defp missing_citation_keys?(citation_target) do
Enum.any?(
[:article_id, :revision_id, :chunk_index],
&(map_lookup(citation_target, &1) in [nil, ""])
)
end
defp metadata_destination(metadata) do
map_lookup(metadata, :destination)
end
defp map_lookup(map, key) do
Map.get(map, key) || Map.get(map, Atom.to_string(key))
end
end