defmodule MedicodeWeb.Components.TranscriptionTextCodingsComponent do @moduledoc """ Represents a portion of transcribed text. """ use Phoenix.Component use MedicodeWeb, :live_component use MedicodeWeb, :verified_routes import Ecto.Query alias Medicode.Transcriptions.TranscriptionChunkCodeVector alias Medicode.Utilities alias Medicode.Repo @impl Phoenix.LiveComponent def update_many(assigns_sockets) do list_of_ids = Enum.map(assigns_sockets, fn {assigns, _sockets} -> assigns.chunk_id end) current_user = assigns_sockets |> Enum.at(0) |> Tuple.to_list() |> Enum.at(0) |> Map.get(:current_user) code_feedbacks = from( f in Medicode.Feedback.CodeFeedback, right_join: c in assoc(f, :transcription_chunk_code_vectors), where: f.user_id == ^current_user.id and f.transcription_chunk_id in ^list_of_ids, order_by: [desc: c.cosine_similarity, asc: c.id], select: [:code_vector_id, :response, :transcription_chunk_id] ) |> Repo.all() transcription_chunk_code_vectors = TranscriptionChunkCodeVector |> join(:inner, [c], v in assoc(c, :code_vector)) |> where([c], c.transcription_chunk_id in ^list_of_ids) |> order_by([c], desc: c.cosine_similarity, asc: c.id) |> preload([:code_vector, :assigned_by_user]) |> Repo.all() Enum.map(assigns_sockets, fn {assigns, socket} -> chunk_code_vectors_with_feedback = transcription_chunk_code_vectors |> Enum.filter(&(&1.transcription_chunk_id == assigns.chunk_id)) |> Enum.reduce([], fn item, acc -> transcription_chunk_code_feedback = Enum.find(code_feedbacks, fn code_feedback -> code_feedback.transcription_chunk_id == item.transcription_chunk_id && code_feedback.code_vector_id == item.code_vector_id end) acc ++ [ %{ transcription_chunk_code_vector: item, code_feedback: transcription_chunk_code_feedback } ] end) socket |> assign(:id, "#{assigns.id}-codings") |> assign(:current_user, assigns.current_user) |> assign(:chunk_code_vectors_with_feedback, chunk_code_vectors_with_feedback) |> assign(:chunk_id, assigns.chunk_id) |> assign(:text, assigns.text) |> assign(:on_feedback, assigns.on_feedback) |> assign(:on_remove_code, assigns.on_remove_code) |> assign(:on_finalize_code, assigns.on_finalize_code) end) end @impl Phoenix.LiveComponent def render(assigns) do ~H"""

No code vectors

<.tag_result :for={ %{ transcription_chunk_code_vector: transcription_chunk_code_vector, code_feedback: code_feedback } <- @chunk_code_vectors_with_feedback } transcription_chunk_code_vector={transcription_chunk_code_vector} chunk_id={@chunk_id} code_vector={transcription_chunk_code_vector.code_vector} score={1.0} text={@text} weighting={[1.0]} current_user={@current_user} myself={@myself} code_feedback={code_feedback} assigned_by_user={transcription_chunk_code_vector.assigned_by_user} />
""" end def tag_result(assigns) do ~H"""
<.feedback_button chunk_id={@chunk_id} code_feedback={@code_feedback} code_vector={@code_vector} text={@text} response="true" myself={@myself} /> <.feedback_button chunk_id={@chunk_id} code_feedback={@code_feedback} code_vector={@code_vector} text={@text} response="false" myself={@myself} />

<%= @code_vector.code %>

<%= @code_vector.description %>

Assigned by: <%= @assigned_by_user.email %>

""" end # Supporting function for displaying a button to provide feedback in `tag_result/1` defp feedback_button(assigns) do ~H""" """ end @impl Phoenix.LiveComponent def handle_event("toggle_feedback", params, socket) do socket.assigns.on_feedback.(params) {:noreply, socket} end def handle_event("remove_code", params, socket) do socket.assigns.on_remove_code.(params) {:noreply, socket} end def handle_event("finalize_code", params, socket) do socket.assigns.on_finalize_code.(params) {:noreply, socket} end # Supporting function for determining background color for code in `tag_result/1` defp code_color(weighting) do cond do :positive in weighting and :negative in weighting -> "bg-orange-200/40" :positive in weighting -> "bg-emerald-200/40" :negative in weighting -> "bg-red-200/40" true -> "" end end # Supporting function for building title attribute for code in `tag_result/1` defp code_title(score, weighting) do weighting_description = if length(weighting) > 0 do weighting |> Utilities.tally() |> Utilities.tally_to_string() |> String.replace_prefix("", "Weighting: ") else "" end "Similarity score: #{trunc(score * 100) / 100}. #{weighting_description}" end defp data_feedback_for_code_vector( %Medicode.Feedback.CodeFeedback{response: true}, "true" = _response ), do: :positive defp data_feedback_for_code_vector( %Medicode.Feedback.CodeFeedback{response: false}, "false" = _response ), do: :negative defp data_feedback_for_code_vector(_, _), do: nil end