File size: 5,196 Bytes
3f219b5
aeeeee4
 
 
 
3f219b5
5f7f244
3f219b5
aeeeee4
3f219b5
 
5f7f244
aeeeee4
 
 
 
 
 
f296c58
aeeeee4
 
 
 
f296c58
5f7f244
92f1e06
5f7f244
 
42d83b8
 
 
 
 
 
5f7f244
92f1e06
42d83b8
 
f296c58
 
aeeeee4
 
 
b25aea3
aeeeee4
96365df
3280f20
 
 
 
 
 
 
 
96365df
 
 
5f7f244
96365df
 
 
 
 
 
 
 
 
 
 
 
aeeeee4
9da0664
 
 
 
 
 
 
 
 
aeeeee4
9da0664
 
aeeeee4
 
 
 
 
 
cad3c5b
 
 
3f219b5
aeeeee4
3280f20
aeeeee4
 
b25aea3
5f7f244
b25aea3
 
3280f20
 
3f219b5
f296c58
 
 
 
 
 
 
 
3280f20
5f7f244
 
 
92f1e06
fad254c
 
5f7f244
 
3f219b5
92f1e06
 
3cf8d48
 
3f219b5
 
5f7f244
 
17a7eb5
 
 
 
 
 
f296c58
5fc337e
 
 
f296c58
 
 
5f7f244
f296c58
 
 
aeeeee4
278333f
 
f296c58
 
 
 
 
 
 
 
278333f
 
 
 
f296c58
278333f
 
 
aeeeee4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
defmodule MedicodeWeb.Components.CodeSelect do
  @moduledoc """
  An auto-complete select field that allows users to search for codes to add by either `code` or `description`.
  """

  use MedicodeWeb, :live_component

  import MedicodeWeb.Components, only: [code_display: 1]

  alias Medicode.Feedback
  alias Medicode.Transcriptions

  @impl Phoenix.LiveComponent
  def mount(socket) do
    socket =
      socket
      |> assign(:codes, [])
      |> assign(:selected_code, nil)
      |> assign(:submitted_code_feedback, nil)

    {:ok, socket}
  end

  @impl Phoenix.LiveComponent
  def update(
        %{id: id, text: text, current_user: current_user, chunk: chunk} = _assigns,
        socket
      ) do
    form = to_form(%{"search_term" => ""}, as: id)

    socket =
      socket
      |> assign(:form, form)
      |> assign(:text, text)
      |> assign(:current_user, current_user)
      |> assign(:chunk, chunk)

    {:ok, socket}
  end

  @impl Phoenix.LiveComponent
  def render(assigns) do
    ~H"""
    <div class="w-full" phx-click-away="clear-codes" phx-target={@myself}>
      <%= if is_nil(@selected_code) do %>
        <.form for={@form} phx-change="suggest-code" phx-target={@myself} class="relative">
          <.input
            type="text"
            field={@form[:search_term]}
            class="w-full rounded"
            style={if length(@codes) > 0, do: "border-radius: 0.25rem 0.25rem 0 0 !important;"}
            placeholder="Search for another code..."
          />

          <div
            :if={length(@codes) > 0}
            class="absolute z-10 w-full bg-white border-l border-r border-b border-zinc-400 rounded-b overflow-hidden"
          >
            <button
              :for={code <- @codes}
              type="button"
              class="w-full text-left px-2 py-1 text-sm border-b border-zinc-100 last:border-b-0 hover:bg-blue-200"
              phx-click="choose-code"
              phx-value-code={code.code}
              phx-target={@myself}
            >
              <%= code.code %>: <%= code.description %>
            </button>
          </div>
        </.form>
      <% else %>
        <div class="px-[14px]">
          <div class="flex justify-between pb-1 border-b border-zinc-300">
            <p class="leading-normal font-bold text-type-black-primary uppercase">
              User-selected code
            </p>
            <button type="button" phx-click="clear-code" phx-target={@myself}>
              <.icon name="hero-x-circle" />
            </button>
          </div>

          <.code_display code={@selected_code.code} label={@selected_code.description} />
        </div>
      <% end %>
    </div>
    """
  end

  @impl Phoenix.LiveComponent
  def handle_event("suggest-code", params, socket) do
    form_id = socket.assigns.form.id
    %{^form_id => %{"search_term" => value}} = params
    suggested_codes = Medicode.Coding.search_for_code_vector(value)

    {:noreply, assign(socket, :codes, suggested_codes)}
  end

  def handle_event("clear-codes", _params, socket) do
    {:noreply, assign(socket, :codes, [])}
  end

  def handle_event("choose-code", %{"code" => code}, socket) do
    selected_code = Enum.find(socket.assigns.codes, &(&1.code == code))
    text_vector = Medicode.Coding.compute_vector_as_list(socket.assigns.text)

    code_feedback =
      Feedback.insert_and_return(%{
        text: socket.assigns.text,
        text_vector: text_vector,
        response: true,
        code_vector_id: selected_code.id
      })

    Transcriptions.upsert_code_vector_for_transcription_chunk(%{
      assigned_by_user_id: socket.assigns.current_user.id,
      code_vector_id: selected_code.id,
      transcription_chunk_id: socket.assigns.chunk.id,
      cosine_similarity:
        Medicode.Coding.get_cosine_similarity(text_vector, selected_code.description_vector)
    })

    send_update(MedicodeWeb.Components.TranscriptionTextComponent,
      id: "transcription-chunk-#{socket.assigns.chunk.id}",
      chunk: socket.assigns.chunk,
      chunk_id: socket.assigns.chunk.id,
      current_user: socket.assigns.current_user,
      on_feedback: &MedicodeWeb.TranscriptionsLive.Show.on_feedback/1,
      on_remove_code: &MedicodeWeb.TranscriptionsLive.Show.on_remove_code/1
    )

    Phoenix.PubSub.broadcast(
      :medicode_pubsub,
      "transcriptions:#{socket.assigns.chunk.transcription_id}",
      {:transcription_updated, socket.assigns.chunk}
    )

    socket =
      if is_nil(code_feedback) do
        put_flash(socket, :error, "Failed to record feedback")
      else
        socket
        |> assign(:selected_code, selected_code)
        |> assign(:submitted_code_feedback, code_feedback)
        |> assign(:codes, [])
      end

    {:noreply, socket}
  end

  @impl Phoenix.LiveComponent
  def handle_event(
        "clear-code",
        _params,
        %{assigns: %{submitted_code_feedback: submitted_code_feedback}} = socket
      ) do
    # Delete previous code feedback
    Feedback.delete(submitted_code_feedback)

    socket =
      socket
      |> assign(:codes, [])
      |> assign(:selected_code, nil)
      |> assign(:submitted_code_feedback, nil)

    {:noreply, socket}
  end
end