timgremore
commited on
Commit
•
8ba1194
1
Parent(s):
1e8f5e5
feat: Mark codes as final for reporting purposes
Browse files- lib/medicode/transcriptions.ex +54 -0
- lib/medicode/transcriptions/transcription_chunk_code_vector.ex +5 -1
- lib/medicode_web/components/components.ex +13 -0
- lib/medicode_web/components/transcription_chunk_codings_component.ex +28 -3
- lib/medicode_web/components/transcription_text_component.ex +2 -0
- lib/medicode_web/live/transcriptions_live/show.ex +33 -2
- priv/repo/migrations/20240301170622_add_finalized_at_and_finalized_by_id_to_transcription_chunk_code_vectors.exs +12 -0
- test/medicode_web/live/transcriptions_live_show_test.exs +66 -4
lib/medicode/transcriptions.ex
CHANGED
@@ -14,6 +14,8 @@ defmodule Medicode.Transcriptions do
|
|
14 |
TranscriptionChunkKeyword
|
15 |
}
|
16 |
|
|
|
|
|
17 |
@doc """
|
18 |
Create transcription record and begin transcribing
|
19 |
|
@@ -177,6 +179,31 @@ defmodule Medicode.Transcriptions do
|
|
177 |
end
|
178 |
end
|
179 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
180 |
@doc """
|
181 |
List transcription chunks by transcription ID.
|
182 |
|
@@ -236,6 +263,33 @@ defmodule Medicode.Transcriptions do
|
|
236 |
Repo.all(query)
|
237 |
end
|
238 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
239 |
@doc """
|
240 |
Creates a transcription.
|
241 |
|
|
|
14 |
TranscriptionChunkKeyword
|
15 |
}
|
16 |
|
17 |
+
alias Medicode.Coding.CodeVector
|
18 |
+
|
19 |
@doc """
|
20 |
Create transcription record and begin transcribing
|
21 |
|
|
|
179 |
end
|
180 |
end
|
181 |
|
182 |
+
def finalize_code_vector_by_chunk_id_and_by_user_id(
|
183 |
+
chunk_id,
|
184 |
+
code_vector_id,
|
185 |
+
user_id
|
186 |
+
) do
|
187 |
+
query =
|
188 |
+
TranscriptionChunkCodeVector
|
189 |
+
|> where(
|
190 |
+
[v],
|
191 |
+
v.transcription_chunk_id == ^chunk_id and v.code_vector_id == ^code_vector_id
|
192 |
+
)
|
193 |
+
|
194 |
+
with %TranscriptionChunkCodeVector{} = chunk_vector <- Medicode.Repo.one(query),
|
195 |
+
changeset <-
|
196 |
+
TranscriptionChunkCodeVector.changeset(chunk_vector, %{
|
197 |
+
finalized_at: DateTime.utc_now(),
|
198 |
+
finalized_by_user_id: user_id
|
199 |
+
}),
|
200 |
+
{:ok, chunk_vector} <- Medicode.Repo.update(changeset) do
|
201 |
+
{:ok, chunk_vector}
|
202 |
+
else
|
203 |
+
res -> res
|
204 |
+
end
|
205 |
+
end
|
206 |
+
|
207 |
@doc """
|
208 |
List transcription chunks by transcription ID.
|
209 |
|
|
|
263 |
Repo.all(query)
|
264 |
end
|
265 |
|
266 |
+
@doc """
|
267 |
+
List transcription chunk vector codes by transcription ID.
|
268 |
+
"""
|
269 |
+
def list_transcription_finalized_codes(transcription_id) do
|
270 |
+
code_vector_query =
|
271 |
+
from(
|
272 |
+
cv in CodeVector,
|
273 |
+
join: tc in TranscriptionChunkCodeVector,
|
274 |
+
on: tc.code_vector_id == cv.id,
|
275 |
+
join: chunk in TranscriptionChunk,
|
276 |
+
on: tc.transcription_chunk_id == chunk.id,
|
277 |
+
where: chunk.transcription_id == ^transcription_id and not is_nil(tc.finalized_at),
|
278 |
+
select: [:id, :code]
|
279 |
+
)
|
280 |
+
|
281 |
+
query =
|
282 |
+
from(
|
283 |
+
tc in TranscriptionChunkCodeVector,
|
284 |
+
join: chunk in TranscriptionChunk,
|
285 |
+
on: tc.transcription_chunk_id == chunk.id,
|
286 |
+
where: chunk.transcription_id == ^transcription_id and not is_nil(tc.finalized_at),
|
287 |
+
preload: [code_vector: ^code_vector_query]
|
288 |
+
)
|
289 |
+
|
290 |
+
Repo.all(query)
|
291 |
+
end
|
292 |
+
|
293 |
@doc """
|
294 |
Creates a transcription.
|
295 |
|
lib/medicode/transcriptions/transcription_chunk_code_vector.ex
CHANGED
@@ -11,10 +11,12 @@ defmodule Medicode.Transcriptions.TranscriptionChunkCodeVector do
|
|
11 |
schema "transcription_chunk_code_vectors" do
|
12 |
field :cosine_similarity, :float
|
13 |
field :weighting, {:array, :string}
|
|
|
14 |
|
15 |
belongs_to :transcription_chunk, Medicode.Transcriptions.TranscriptionChunk
|
16 |
belongs_to :code_vector, Medicode.Coding.CodeVector
|
17 |
belongs_to :assigned_by_user, Medicode.Accounts.User
|
|
|
18 |
|
19 |
timestamps(type: :utc_datetime)
|
20 |
end
|
@@ -27,7 +29,9 @@ defmodule Medicode.Transcriptions.TranscriptionChunkCodeVector do
|
|
27 |
:code_vector_id,
|
28 |
:cosine_similarity,
|
29 |
:weighting,
|
30 |
-
:assigned_by_user_id
|
|
|
|
|
31 |
])
|
32 |
|> validate_required([
|
33 |
:transcription_chunk_id,
|
|
|
11 |
schema "transcription_chunk_code_vectors" do
|
12 |
field :cosine_similarity, :float
|
13 |
field :weighting, {:array, :string}
|
14 |
+
field :finalized_at, :utc_datetime
|
15 |
|
16 |
belongs_to :transcription_chunk, Medicode.Transcriptions.TranscriptionChunk
|
17 |
belongs_to :code_vector, Medicode.Coding.CodeVector
|
18 |
belongs_to :assigned_by_user, Medicode.Accounts.User
|
19 |
+
belongs_to :finalized_by_user, Medicode.Accounts.User
|
20 |
|
21 |
timestamps(type: :utc_datetime)
|
22 |
end
|
|
|
29 |
:code_vector_id,
|
30 |
:cosine_similarity,
|
31 |
:weighting,
|
32 |
+
:assigned_by_user_id,
|
33 |
+
:finalized_by_user_id,
|
34 |
+
:finalized_at
|
35 |
])
|
36 |
|> validate_required([
|
37 |
:transcription_chunk_id,
|
lib/medicode_web/components/components.ex
CHANGED
@@ -72,6 +72,7 @@ defmodule MedicodeWeb.Components do
|
|
72 |
attr(:target, :any, required: true)
|
73 |
attr(:transcription, Medicode.Transcriptions.Transcription, required: true)
|
74 |
attr(:summary_keywords, :list, default: [])
|
|
|
75 |
|
76 |
@doc """
|
77 |
Shows the status and keywords for the current session.
|
@@ -132,6 +133,18 @@ defmodule MedicodeWeb.Components do
|
|
132 |
<% end %>
|
133 |
</div>
|
134 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
135 |
</div>
|
136 |
"""
|
137 |
end
|
|
|
72 |
attr(:target, :any, required: true)
|
73 |
attr(:transcription, Medicode.Transcriptions.Transcription, required: true)
|
74 |
attr(:summary_keywords, :list, default: [])
|
75 |
+
attr(:finalized_codes, :list, default: [])
|
76 |
|
77 |
@doc """
|
78 |
Shows the status and keywords for the current session.
|
|
|
133 |
<% end %>
|
134 |
</div>
|
135 |
</div>
|
136 |
+
<div :if={!Enum.empty?(@finalized_codes)} class="px-4 pt-2 pb-10 flex flex-col gap-2">
|
137 |
+
<p class="leading-normal font-bold text-type-black-primary uppercase">Finalized Codes</p>
|
138 |
+
|
139 |
+
<div
|
140 |
+
class="flex flex-row items-center divide-x divide-black/15 text-sm leading-normal text-type-black-tertiary"
|
141 |
+
id="finalized_vector_code_list"
|
142 |
+
>
|
143 |
+
<span :for={chunk_code <- @finalized_codes} class="px-2" title={chunk_code.code_vector.code}>
|
144 |
+
<%= chunk_code.code_vector.code %>
|
145 |
+
</span>
|
146 |
+
</div>
|
147 |
+
</div>
|
148 |
</div>
|
149 |
"""
|
150 |
end
|
lib/medicode_web/components/transcription_chunk_codings_component.ex
CHANGED
@@ -18,8 +18,9 @@ defmodule MedicodeWeb.Components.TranscriptionTextCodingsComponent do
|
|
18 |
{%TranscriptionChunkCodeVector{
|
19 |
code_vector: code_vector,
|
20 |
assigned_by_user: assigned_by_user
|
21 |
-
}, feedback} <- @code_vectors_with_feedback
|
22 |
}
|
|
|
23 |
chunk_id={@chunk_id}
|
24 |
code_vector={code_vector}
|
25 |
score={1.0}
|
@@ -38,7 +39,7 @@ defmodule MedicodeWeb.Components.TranscriptionTextCodingsComponent do
|
|
38 |
~H"""
|
39 |
<div
|
40 |
phx-mounted={JS.transition({"ease-out", "opacity-0", "opacity-100"})}
|
41 |
-
class="ease-out duration-300 opacity-0 transition-all flex items-center gap-4 px-[14px] py-3 text-sm"
|
42 |
>
|
43 |
<div class="flex gap-3">
|
44 |
<.feedback_button
|
@@ -62,10 +63,29 @@ defmodule MedicodeWeb.Components.TranscriptionTextCodingsComponent do
|
|
62 |
<div class="w-1 h-full border-l border-[#444444]/20"></div>
|
63 |
|
64 |
<div
|
|
|
65 |
class={"w-full flex flex-col gap-1 font-secondary text-type-black-primary ml-4 px-2 py-1 rounded #{code_color(@weighting)}"}
|
66 |
title={code_title(@score, @weighting)}
|
67 |
>
|
68 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
69 |
<p class="text-base leading-[20.42px]"><%= @code_vector.description %></p>
|
70 |
<div class="flex flex-row items-center w-full gap-4">
|
71 |
<p :if={!is_nil(@assigned_by_user)} class="text-xs text-base leading-[20.42px]">
|
@@ -133,6 +153,11 @@ defmodule MedicodeWeb.Components.TranscriptionTextCodingsComponent do
|
|
133 |
{:noreply, socket}
|
134 |
end
|
135 |
|
|
|
|
|
|
|
|
|
|
|
136 |
# Supporting function for determining background color for code in `tag_result/1`
|
137 |
defp code_color(weighting) do
|
138 |
cond do
|
|
|
18 |
{%TranscriptionChunkCodeVector{
|
19 |
code_vector: code_vector,
|
20 |
assigned_by_user: assigned_by_user
|
21 |
+
} = transcription_chunk_code_vector, feedback} <- @code_vectors_with_feedback
|
22 |
}
|
23 |
+
transcription_chunk_code_vector={transcription_chunk_code_vector}
|
24 |
chunk_id={@chunk_id}
|
25 |
code_vector={code_vector}
|
26 |
score={1.0}
|
|
|
39 |
~H"""
|
40 |
<div
|
41 |
phx-mounted={JS.transition({"ease-out", "opacity-0", "opacity-100"})}
|
42 |
+
class="group ease-out duration-300 opacity-0 transition-all flex items-center gap-4 px-[14px] py-3 text-sm"
|
43 |
>
|
44 |
<div class="flex gap-3">
|
45 |
<.feedback_button
|
|
|
63 |
<div class="w-1 h-full border-l border-[#444444]/20"></div>
|
64 |
|
65 |
<div
|
66 |
+
id={"chunk-#{@chunk_id}-coding-#{@code_vector.id}"}
|
67 |
class={"w-full flex flex-col gap-1 font-secondary text-type-black-primary ml-4 px-2 py-1 rounded #{code_color(@weighting)}"}
|
68 |
title={code_title(@score, @weighting)}
|
69 |
>
|
70 |
+
<div class="flex flex-row justify-between">
|
71 |
+
<p class="text-lg font-bold leading-[22.97px]"><%= @code_vector.code %></p>
|
72 |
+
<button
|
73 |
+
:if={is_nil(@transcription_chunk_code_vector.finalized_at)}
|
74 |
+
type="button"
|
75 |
+
class="transition-all duration-300 opacity-0 group-hover:opacity-100 hover:bg-slate-200 px-2 border border-1 border-iron-mountain rounded-md"
|
76 |
+
phx-click="finalize_code"
|
77 |
+
phx-value-chunk_id={@chunk_id}
|
78 |
+
phx-value-code_vector_id={@code_vector.id}
|
79 |
+
phx-target={@myself}
|
80 |
+
>
|
81 |
+
Finalize
|
82 |
+
</button>
|
83 |
+
<.icon
|
84 |
+
:if={!is_nil(@transcription_chunk_code_vector.finalized_at)}
|
85 |
+
name="hero-check-badge"
|
86 |
+
class="opacity-40 group-hover:opacity-70"
|
87 |
+
/>
|
88 |
+
</div>
|
89 |
<p class="text-base leading-[20.42px]"><%= @code_vector.description %></p>
|
90 |
<div class="flex flex-row items-center w-full gap-4">
|
91 |
<p :if={!is_nil(@assigned_by_user)} class="text-xs text-base leading-[20.42px]">
|
|
|
153 |
{:noreply, socket}
|
154 |
end
|
155 |
|
156 |
+
def handle_event("finalize_code", params, socket) do
|
157 |
+
socket.assigns.on_finalize_code.(params)
|
158 |
+
{:noreply, socket}
|
159 |
+
end
|
160 |
+
|
161 |
# Supporting function for determining background color for code in `tag_result/1`
|
162 |
defp code_color(weighting) do
|
163 |
cond do
|
lib/medicode_web/components/transcription_text_component.ex
CHANGED
@@ -48,6 +48,7 @@ defmodule MedicodeWeb.Components.TranscriptionTextComponent do
|
|
48 |
|> assign(:code_vectors_with_feedback, code_vectors_with_feedback)
|
49 |
|> assign(:on_feedback, assigns.on_feedback)
|
50 |
|> assign(:on_remove_code, assigns.on_remove_code)
|
|
|
51 |
end)
|
52 |
end
|
53 |
|
@@ -111,6 +112,7 @@ defmodule MedicodeWeb.Components.TranscriptionTextComponent do
|
|
111 |
code_vectors_with_feedback={@code_vectors_with_feedback}
|
112 |
on_feedback={@on_feedback}
|
113 |
on_remove_code={@on_remove_code}
|
|
|
114 |
/>
|
115 |
<.live_component
|
116 |
:if={!@classification_loading}
|
|
|
48 |
|> assign(:code_vectors_with_feedback, code_vectors_with_feedback)
|
49 |
|> assign(:on_feedback, assigns.on_feedback)
|
50 |
|> assign(:on_remove_code, assigns.on_remove_code)
|
51 |
+
|> assign(:on_finalize_code, assigns.on_finalize_code)
|
52 |
end)
|
53 |
end
|
54 |
|
|
|
112 |
code_vectors_with_feedback={@code_vectors_with_feedback}
|
113 |
on_feedback={@on_feedback}
|
114 |
on_remove_code={@on_remove_code}
|
115 |
+
on_finalize_code={@on_finalize_code}
|
116 |
/>
|
117 |
<.live_component
|
118 |
:if={!@classification_loading}
|
lib/medicode_web/live/transcriptions_live/show.ex
CHANGED
@@ -20,6 +20,7 @@ defmodule MedicodeWeb.TranscriptionsLive.Show do
|
|
20 |
transcription_chunk_ids = Enum.map(transcription.chunks, &%{id: &1.id})
|
21 |
|
22 |
summary_keywords = Transcriptions.list_transcription_summary_keywords(transcription.id)
|
|
|
23 |
|
24 |
initial_state = %{
|
25 |
current_recording_id: 0,
|
@@ -29,7 +30,7 @@ defmodule MedicodeWeb.TranscriptionsLive.Show do
|
|
29 |
summary_keywords: summary_keywords,
|
30 |
transcription: transcription,
|
31 |
transcriptions: list_transcriptions(session["current_user"]),
|
32 |
-
finalized_codes:
|
33 |
}
|
34 |
|
35 |
socket =
|
@@ -57,6 +58,7 @@ defmodule MedicodeWeb.TranscriptionsLive.Show do
|
|
57 |
target={self()}
|
58 |
transcription={@transcription}
|
59 |
summary_keywords={@summary_keywords}
|
|
|
60 |
/>
|
61 |
|
62 |
<img
|
@@ -76,6 +78,7 @@ defmodule MedicodeWeb.TranscriptionsLive.Show do
|
|
76 |
finalized_codes={@finalized_codes}
|
77 |
on_feedback={&on_feedback/1}
|
78 |
on_remove_code={&on_remove_code/1}
|
|
|
79 |
/>
|
80 |
</div>
|
81 |
</div>
|
@@ -92,6 +95,10 @@ defmodule MedicodeWeb.TranscriptionsLive.Show do
|
|
92 |
send(self(), {"remove_code", params})
|
93 |
end
|
94 |
|
|
|
|
|
|
|
|
|
95 |
@impl Phoenix.LiveView
|
96 |
def handle_event("reset_to_pending", _params, socket) do
|
97 |
{:noreply, assign(socket, :status, :pending)}
|
@@ -260,6 +267,28 @@ defmodule MedicodeWeb.TranscriptionsLive.Show do
|
|
260 |
{:noreply, socket}
|
261 |
end
|
262 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
263 |
def handle_info({ref, _result}, socket) do
|
264 |
# See this Fly article for the usage of Task.async to start `transcribe_and_tag_audio/2` and handle the end of the
|
265 |
# task here: https://fly.io/phoenix-files/liveview-async-task/
|
@@ -275,7 +304,9 @@ defmodule MedicodeWeb.TranscriptionsLive.Show do
|
|
275 |
classification_loading: classification_loading,
|
276 |
current_user: socket.assigns.current_user,
|
277 |
on_feedback: &on_feedback/1,
|
278 |
-
on_remove_code: &on_remove_code/1
|
|
|
|
|
279 |
)
|
280 |
|
281 |
chunk = Transcriptions.get_transcription_chunk!(chunk_id)
|
|
|
20 |
transcription_chunk_ids = Enum.map(transcription.chunks, &%{id: &1.id})
|
21 |
|
22 |
summary_keywords = Transcriptions.list_transcription_summary_keywords(transcription.id)
|
23 |
+
finalized_codes = Transcriptions.list_transcription_finalized_codes(transcription.id)
|
24 |
|
25 |
initial_state = %{
|
26 |
current_recording_id: 0,
|
|
|
30 |
summary_keywords: summary_keywords,
|
31 |
transcription: transcription,
|
32 |
transcriptions: list_transcriptions(session["current_user"]),
|
33 |
+
finalized_codes: finalized_codes
|
34 |
}
|
35 |
|
36 |
socket =
|
|
|
58 |
target={self()}
|
59 |
transcription={@transcription}
|
60 |
summary_keywords={@summary_keywords}
|
61 |
+
finalized_codes={@finalized_codes}
|
62 |
/>
|
63 |
|
64 |
<img
|
|
|
78 |
finalized_codes={@finalized_codes}
|
79 |
on_feedback={&on_feedback/1}
|
80 |
on_remove_code={&on_remove_code/1}
|
81 |
+
on_finalize_code={&on_finalize_code/1}
|
82 |
/>
|
83 |
</div>
|
84 |
</div>
|
|
|
95 |
send(self(), {"remove_code", params})
|
96 |
end
|
97 |
|
98 |
+
def on_finalize_code(params) do
|
99 |
+
send(self(), {"finalize_code", params})
|
100 |
+
end
|
101 |
+
|
102 |
@impl Phoenix.LiveView
|
103 |
def handle_event("reset_to_pending", _params, socket) do
|
104 |
{:noreply, assign(socket, :status, :pending)}
|
|
|
267 |
{:noreply, socket}
|
268 |
end
|
269 |
|
270 |
+
def handle_info(
|
271 |
+
{"finalize_code", %{"chunk_id" => chunk_id, "code_vector_id" => code_vector_id}},
|
272 |
+
socket
|
273 |
+
) do
|
274 |
+
Transcriptions.finalize_code_vector_by_chunk_id_and_by_user_id(
|
275 |
+
chunk_id,
|
276 |
+
code_vector_id,
|
277 |
+
socket.assigns.current_user.id
|
278 |
+
)
|
279 |
+
|
280 |
+
send_transcription_text_update(chunk_id, false, socket)
|
281 |
+
|
282 |
+
socket =
|
283 |
+
socket
|
284 |
+
|> assign(
|
285 |
+
:finalized_codes,
|
286 |
+
Transcriptions.list_transcription_finalized_codes(socket.assigns.transcription.id)
|
287 |
+
)
|
288 |
+
|
289 |
+
{:noreply, socket}
|
290 |
+
end
|
291 |
+
|
292 |
def handle_info({ref, _result}, socket) do
|
293 |
# See this Fly article for the usage of Task.async to start `transcribe_and_tag_audio/2` and handle the end of the
|
294 |
# task here: https://fly.io/phoenix-files/liveview-async-task/
|
|
|
304 |
classification_loading: classification_loading,
|
305 |
current_user: socket.assigns.current_user,
|
306 |
on_feedback: &on_feedback/1,
|
307 |
+
on_remove_code: &on_remove_code/1,
|
308 |
+
on_finalize_code: &on_finalize_code/1,
|
309 |
+
finalized_codes: socket.assigns.finalized_codes
|
310 |
)
|
311 |
|
312 |
chunk = Transcriptions.get_transcription_chunk!(chunk_id)
|
priv/repo/migrations/20240301170622_add_finalized_at_and_finalized_by_id_to_transcription_chunk_code_vectors.exs
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
defmodule Medicode.Repo.Migrations.AddFinalizedAtAndFinalizedByIdToTranscriptionChunkCodeVectors do
|
2 |
+
use Ecto.Migration
|
3 |
+
|
4 |
+
def change do
|
5 |
+
alter table(:transcription_chunk_code_vectors) do
|
6 |
+
add :finalized_at, :utc_datetime, null: true
|
7 |
+
|
8 |
+
add :finalized_by_user_id, references(:users, type: :binary_id, on_delete: :nilify_all),
|
9 |
+
null: true
|
10 |
+
end
|
11 |
+
end
|
12 |
+
end
|
test/medicode_web/live/transcriptions_live_show_test.exs
CHANGED
@@ -6,7 +6,9 @@ defmodule MedicodeWeb.TranscriptionsLive.ShowTest do
|
|
6 |
import Medicode.{
|
7 |
AccountsFixtures,
|
8 |
TranscriptionsFixtures,
|
9 |
-
TranscriptionChunksFixtures
|
|
|
|
|
10 |
}
|
11 |
|
12 |
setup %{conn: conn} do
|
@@ -14,10 +16,37 @@ defmodule MedicodeWeb.TranscriptionsLive.ShowTest do
|
|
14 |
user = user_fixture(%{password: password})
|
15 |
transcription = transcription_fixture(%{filename: "my-audio.mp3"})
|
16 |
|
17 |
-
|
18 |
-
|
19 |
|
20 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
21 |
end
|
22 |
|
23 |
describe "/" do
|
@@ -35,5 +64,38 @@ defmodule MedicodeWeb.TranscriptionsLive.ShowTest do
|
|
35 |
invalid_id = Ecto.UUID.generate()
|
36 |
assert_raise Medicode.Fallback, fn -> get(conn, "/transcriptions/#{invalid_id}") end
|
37 |
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
38 |
end
|
39 |
end
|
|
|
6 |
import Medicode.{
|
7 |
AccountsFixtures,
|
8 |
TranscriptionsFixtures,
|
9 |
+
TranscriptionChunksFixtures,
|
10 |
+
TranscriptionChunkCodeVectorsFixtures,
|
11 |
+
CodeVectorsFixtures
|
12 |
}
|
13 |
|
14 |
setup %{conn: conn} do
|
|
|
16 |
user = user_fixture(%{password: password})
|
17 |
transcription = transcription_fixture(%{filename: "my-audio.mp3"})
|
18 |
|
19 |
+
transcription_chunk1 =
|
20 |
+
transcription_chunk_fixture(%{transcription_id: transcription.id, text: "Bar"})
|
21 |
|
22 |
+
transcription_chunk2 =
|
23 |
+
transcription_chunk_fixture(%{transcription_id: transcription.id, text: "Foo"})
|
24 |
+
|
25 |
+
code_vector1 = code_vector_fixture(%{code: "001"})
|
26 |
+
code_vector2 = code_vector_fixture(%{code: "002"})
|
27 |
+
|
28 |
+
transcription_chunk_code_vector_fixture(%{
|
29 |
+
transcription_chunk_id: transcription_chunk1.id,
|
30 |
+
code_vector_id: code_vector1.id
|
31 |
+
})
|
32 |
+
|
33 |
+
transcription_chunk_code_vector_fixture(%{
|
34 |
+
transcription_chunk_id: transcription_chunk1.id,
|
35 |
+
code_vector_id: code_vector2.id
|
36 |
+
})
|
37 |
+
|
38 |
+
transcription_chunk_code_vector_fixture(%{
|
39 |
+
transcription_chunk_id: transcription_chunk2.id,
|
40 |
+
code_vector_id: code_vector2.id
|
41 |
+
})
|
42 |
+
|
43 |
+
%{
|
44 |
+
conn: log_in_user(conn, user),
|
45 |
+
current_user: user,
|
46 |
+
transcription: transcription,
|
47 |
+
transcription_chunk1: transcription_chunk1,
|
48 |
+
code_vector1: code_vector1
|
49 |
+
}
|
50 |
end
|
51 |
|
52 |
describe "/" do
|
|
|
64 |
invalid_id = Ecto.UUID.generate()
|
65 |
assert_raise Medicode.Fallback, fn -> get(conn, "/transcriptions/#{invalid_id}") end
|
66 |
end
|
67 |
+
|
68 |
+
test "renders codes as finalized", %{
|
69 |
+
conn: conn,
|
70 |
+
transcription: transcription,
|
71 |
+
transcription_chunk1: transcription_chunk1,
|
72 |
+
code_vector1: code_vector1
|
73 |
+
} do
|
74 |
+
conn = get(conn, "/transcriptions/#{transcription.id}")
|
75 |
+
assert html_response(conn, 200) =~ "MediCode"
|
76 |
+
|
77 |
+
{:ok, view, _html} = live(conn)
|
78 |
+
|
79 |
+
# NOTE: Since MedicodeWeb.TranscriptionsLive.Show.send_transcription_text_update/3 relies on Phoenix.LiveView.send_update/3,
|
80 |
+
# we need to call `render(view)` to get the async update rendered before asserting.
|
81 |
+
#
|
82 |
+
# Additionally, we are using PubSub to update child components. The call to `:sys.get_state(view.pid)` ensures the mailbox is empty
|
83 |
+
# and it's safe to assert results.
|
84 |
+
# See: https://elixirforum.com/t/testing-liveviews-that-rely-on-pubsub-for-updates/40938/5
|
85 |
+
view
|
86 |
+
|> element(
|
87 |
+
"#chunk-#{transcription_chunk1.id}-coding-#{code_vector1.id} button",
|
88 |
+
"Finalize"
|
89 |
+
)
|
90 |
+
|> render_click()
|
91 |
+
|
92 |
+
_ = :sys.get_state(view.pid)
|
93 |
+
|
94 |
+
rendered = render(view)
|
95 |
+
|
96 |
+
assert rendered =~ "<span class=\"hero-check-badge"
|
97 |
+
assert rendered =~ "Finalized Codes"
|
98 |
+
assert rendered =~ "001"
|
99 |
+
end
|
100 |
end
|
101 |
end
|