|
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) |
|
|
|
|
|
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 |
|
|