medicode / lib /medical_transcription /transcriptions.ex
timgremore's picture
fix: Restore keywords summary
5c502c9
raw
history blame
9.67 kB
defmodule Medicode.Transcriptions do
@moduledoc """
The Transcriptions context.
"""
import Ecto.Query, warn: false
alias Medicode.Repo
alias Medicode.Transcriptions.{
Transcription,
TranscriptionChunk,
TranscriptionChunkKeyword,
TranscriptionChunkCodeVector
}
@doc """
Create transcription record and begin transcribing
## Examples
iex> create_and_transcribe_audio("my-audio.mp3")
%Transcription{audio_file: "my-audio.mp3"}
"""
def transcribe_audio(transcription) do
Medicode.TranscriptionSupervisor.start_transcription(transcription)
end
@doc """
Returns the list of transcriptions.
## Examples
iex> list_transcriptions()
[%Transcription{}, ...]
"""
def list_transcriptions(user) do
Transcription
|> where([t], t.user_id == ^user.id)
|> Repo.all()
end
@doc """
Gets a single transcription.
Returns `nil` if the Transcription does not exist.
## Examples
iex> get_transcription(123)
%Transcription{}
iex> get_transcription(456)
nil
"""
def get_transcription(id, preload_transcription_chunks \\ false) do
query =
if preload_transcription_chunks do
chunks_query =
TranscriptionChunk
|> preload([:keywords, :code_vectors])
|> order_by([c], asc: c.start_mark)
Transcription
|> preload(chunks: ^chunks_query)
else
Transcription
end
Repo.get(query, id)
end
@doc """
Gets a single transcription.
Raises `Ecto.NoResultsError` if the Transcription does not exist.
## Examples
iex> get_transcription!(123)
%Transcription{}
iex> get_transcription!(456)
** (Ecto.NoResultsError)
"""
def get_transcription!(id, preload_transcription_chunks \\ false) do
query =
if preload_transcription_chunks do
Transcription
|> preload(chunks: [:keywords, :code_vectors])
else
Transcription
end
Repo.get!(query, id)
end
@doc """
Gets a single transcription chunk.
Raises `Ecto.NoResultsError` if the TranscriptionChunk does not exist.
## Examples
iex> get_transcription_chunk!(123)
%TranscriptionChunk{}
iex> get_transcription_chunk!(456)
** (Ecto.NoResultsError)
"""
def get_transcription_chunk!(id, preload_transcription_chunk_associations \\ false) do
query =
if preload_transcription_chunk_associations do
TranscriptionChunk
|> preload([:keywords, :code_vectors])
else
TranscriptionChunk
end
Repo.get!(query, id)
end
@doc """
Lists code vectors for transcription chunk ID.
Returns `[]` if none are found.
## Examples
iex> list_transcription_chunk_code_vectors(123)
%CodeVector{}
iex> list_transcription_chunk_code_vectors(456)
[]
"""
def list_transcription_chunk_code_vectors(chunk_id) do
TranscriptionChunkCodeVector
|> join(:inner, [chunk_vector], code_vector in assoc(chunk_vector, :code_vector))
|> where([chunk_vector], chunk_vector.transcription_chunk_id == ^chunk_id)
|> order_by([chunk_vector], desc: chunk_vector.cosine_similarity)
|> preload([:assigned_by_user, :code_vector])
|> Repo.all()
end
def delete_code_vector_by_chunk_id_and_assigned_by_user_id(
chunk_id,
code_vector_id,
assigned_by_user_id
) do
query =
TranscriptionChunkCodeVector
|> where(
[v],
v.transcription_chunk_id == ^chunk_id and v.code_vector_id == ^code_vector_id and
v.assigned_by_user_id == ^assigned_by_user_id
)
with %TranscriptionChunkCodeVector{} = chunk_vector <- Medicode.Repo.one(query),
{:ok, _struct} <- Medicode.Repo.delete(chunk_vector) do
{:ok, chunk_vector}
else
res -> res
end
end
@doc """
List transcription chunks by transcription ID.
Returns [] if none are found.
## Examples
iex> list_transcription_chunks(123)
[%TranscriptionChunk{}]
iex> list_transcription_chunks(456)
[]
"""
def list_transcription_chunks(
transcription_chunk_ids,
preload_transcription_chunk_associations \\ false
) do
query =
if preload_transcription_chunk_associations do
TranscriptionChunk
|> preload([:keywords, :code_vectors])
else
TranscriptionChunk
end
query
|> where([c], c.id in ^transcription_chunk_ids)
|> select([c], {c.id, c})
|> Repo.all()
end
@doc """
Collect transcription keywords and order by score.
"""
def list_transcription_summary_keywords(transcription_id) do
query =
from(
k in TranscriptionChunkKeyword,
join: c in TranscriptionChunk, on: k.transcription_chunk_id == c.id,
where: c.transcription_id == ^transcription_id,
group_by: k.keyword,
select: %{keyword: k.keyword, score: sum(k.score)},
order_by: [desc: sum(k.score)]
)
Repo.all(query)
end
@doc """
Creates a transcription.
## Examples
iex> create_transcription(%{field: value})
{:ok, %Transcription{}}
iex> create_transcription(%{field: bad_value})
{:error, %Ecto.Changeset{}}
"""
def create_transcription(attrs \\ %{}) do
%Transcription{}
|> Transcription.changeset(attrs)
|> Repo.insert()
end
@doc """
Creates a transcription chunk.
## Examples
iex> create_chunk(%{field: value})
{:ok, %TranscriptionChunk{}}
iex> create_chunk(%{field: bad_value})
{:error, %Ecto.Changeset{}}
"""
def create_chunk(attrs \\ %{}) do
%TranscriptionChunk{}
|> TranscriptionChunk.changeset(attrs)
|> Repo.insert()
end
@doc """
Creates a transcription chunk keyword.
## Examples
iex> create_keyword_for_chunk(%{transcription_chunk_id: 1, keyword: "healthy"})
{:ok, %TranscriptionChunkKeyword{}}
iex> create_keyword_for_chunk(%{field: bad_value})
{:error, %Ecto.Changeset{}}
"""
def create_keyword_for_chunk(attrs \\ %{}) do
%TranscriptionChunkKeyword{}
|> TranscriptionChunkKeyword.changeset(attrs)
|> Repo.insert()
end
@doc """
Creates a transcription chunk code vector record.
## Examples
iex> replace_all_code_vectors_for_chunk(%TranscriptionChunk{}, [%{transcription_chunk_id: 1, code_vector_id: 1, cosine_similarity: 1, weight: "positive"}])
{:ok, [%{id: id}]}
iex> replace_all_code_vectors_for_chunk(%TranscriptionChunk{}, [%{field: bad_value}])
{:error, %Ecto.Changeset{}}
"""
def replace_all_code_vectors_for_chunk(%TranscriptionChunk{id: transcription_chunk_id}, attrs) do
timestamp =
DateTime.utc_now()
|> DateTime.truncate(:second)
{_count, chunk_code_vectors} =
TranscriptionChunkCodeVector
|> Repo.insert_all(
attrs,
placeholders: %{timestamp: timestamp},
on_conflict: {:replace, [:cosine_similarity, :weighting, :updated_at]},
returning: [:id],
conflict_target: [:transcription_chunk_id, :code_vector_id]
)
chunk_code_vector_ids = Enum.map(chunk_code_vectors, & &1.id)
# Clear previous code vectors except for those assigned by a user
from(v in TranscriptionChunkCodeVector,
where:
v.transcription_chunk_id == ^transcription_chunk_id and v.id not in ^chunk_code_vector_ids and
is_nil(v.assigned_by_user_id)
)
|> Repo.delete_all()
chunk_code_vectors
end
@doc """
Insert a transcription chunk code vector record.
## Examples
iex> upsert_code_vector_for_transcription_chunk(%{user_id: user.id, chunk_id: chunk.id})
{:ok, %TranscriptionChunkCodeVector{}}
iex> upsert_code_vector_for_transcription_chunk(%{field: bad_value})
{:error, %Ecto.Changeset{}}
"""
def upsert_code_vector_for_transcription_chunk(attrs) do
%TranscriptionChunkCodeVector{}
|> TranscriptionChunkCodeVector.changeset(attrs)
|> Repo.insert(
on_conflict: :nothing,
conflict_target: [:transcription_chunk_id, :code_vector_id]
)
end
@doc """
Updates a transcription.
## Examples
iex> update_transcription(transcription, %{field: new_value})
{:ok, %Transcription{}}
iex> update_transcription(transcription, %{field: bad_value})
{:error, %Ecto.Changeset{}}
"""
def update_transcription(%Transcription{} = transcription, attrs) do
transcription
|> Transcription.changeset(attrs)
|> Repo.update()
end
@doc """
Updates a transcription chunk.
## Examples
iex> update_transcription_chunk(transcription_chunk, %{field: new_value})
{:ok, %TranscriptionChunk{}}
iex> update_transcription_chunk(transcription_chunk, %{field: bad_value})
{:error, %Ecto.Changeset{}}
"""
def update_transcription_chunk(%TranscriptionChunk{} = chunk, attrs) do
chunk
|> TranscriptionChunk.changeset(attrs)
|> Repo.update()
end
@doc """
Deletes a transcription.
## Examples
iex> delete_transcription(transcription)
{:ok, %Transcription{}}
iex> delete_transcription(transcription)
{:error, %Ecto.Changeset{}}
"""
def delete_transcription(%Transcription{} = transcription) do
Repo.delete(transcription)
end
@doc """
Returns an `%Ecto.Changeset{}` for tracking transcription changes.
## Examples
iex> change_transcription(transcription)
%Ecto.Changeset{data: %Transcription{}}
"""
def change_transcription(%Transcription{} = transcription, attrs \\ %{}) do
Transcription.changeset(transcription, attrs)
end
end