Commit
•
3f219b5
1
Parent(s):
f665ffc
chore: Rename app to Medicode
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- README.md +2 -2
- config/config.exs +8 -8
- config/dev.exs +4 -4
- config/prod.exs +2 -2
- config/runtime.exs +7 -7
- config/test.exs +4 -4
- lib/medical_transcription.ex +2 -2
- lib/medical_transcription/accounts.ex +3 -3
- lib/medical_transcription/accounts/user.ex +5 -5
- lib/medical_transcription/accounts/user_notifier.ex +3 -3
- lib/medical_transcription/accounts/user_token.ex +3 -3
- lib/medical_transcription/application.ex +15 -15
- lib/medical_transcription/audio/recording_pipeline.ex +2 -2
- lib/medical_transcription/audio/transcription_filter.ex +3 -3
- lib/medical_transcription/audio/utilities.ex +1 -1
- lib/medical_transcription/classification_server.ex +5 -5
- lib/medical_transcription/classification_supervisor.ex +2 -2
- lib/medical_transcription/coding.ex +6 -6
- lib/medical_transcription/coding/code_vector.ex +2 -2
- lib/medical_transcription/coding/code_vector_match.ex +1 -1
- lib/medical_transcription/coding/vector_precomputation.ex +2 -2
- lib/medical_transcription/feedback.ex +3 -3
- lib/medical_transcription/feedback/code_feedback.ex +3 -3
- lib/medical_transcription/mailer.ex +2 -2
- lib/medical_transcription/release.ex +4 -4
- lib/medical_transcription/repo.ex +2 -2
- lib/medical_transcription/transcription_server.ex +5 -5
- lib/medical_transcription/transcription_supervisor.ex +2 -2
- lib/medical_transcription/transcriptions.ex +6 -6
- lib/medical_transcription/transcriptions/transcription.ex +3 -3
- lib/medical_transcription/transcriptions/transcription_chunk.ex +5 -5
- lib/medical_transcription/transcriptions/transcription_chunk_code_vector.ex +4 -4
- lib/medical_transcription/transcriptions/transcription_chunk_keyword.ex +2 -2
- lib/medical_transcription/utilities.ex +1 -1
- lib/medical_transcription_web.ex +12 -12
- lib/medical_transcription_web/components/code_select.ex +10 -10
- lib/medical_transcription_web/components/components.ex +6 -6
- lib/medical_transcription_web/components/core_components.ex +4 -4
- lib/medical_transcription_web/components/keyword_highlighter.ex +2 -2
- lib/medical_transcription_web/components/layouts.ex +2 -2
- lib/medical_transcription_web/components/layouts/root.html.heex +1 -1
- lib/medical_transcription_web/components/sidebar_component.ex +4 -4
- lib/medical_transcription_web/components/transcription_chunk_codings_component.ex +7 -7
- lib/medical_transcription_web/components/transcription_text_component.ex +9 -9
- lib/medical_transcription_web/controllers/error_html.ex +2 -2
- lib/medical_transcription_web/controllers/error_html/404.html.heex +1 -1
- lib/medical_transcription_web/controllers/error_json.ex +1 -1
- lib/medical_transcription_web/controllers/page_controller.ex +2 -2
- lib/medical_transcription_web/controllers/page_html.ex +2 -2
- lib/medical_transcription_web/controllers/user_session_controller.ex +4 -4
README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
#
|
2 |
|
3 |
## Getting started
|
4 |
|
@@ -28,7 +28,7 @@ The app is configured to deploy to Fly.io via a `fly.toml` file. To deploy, run
|
|
28 |
To build the code vectors for the ICD-9 codelist for the deployed environment:
|
29 |
|
30 |
1. Connect to the server with `fly ssh console`.
|
31 |
-
2. Run `/app/bin/medical_transcription eval
|
32 |
|
33 |
### Livebook
|
34 |
|
|
|
1 |
+
# Medicode
|
2 |
|
3 |
## Getting started
|
4 |
|
|
|
28 |
To build the code vectors for the ICD-9 codelist for the deployed environment:
|
29 |
|
30 |
1. Connect to the server with `fly ssh console`.
|
31 |
+
2. Run `/app/bin/medical_transcription eval Medicode.Release.precompute_code_vectors`. This will prepare the vectors in the database if they are not present.
|
32 |
|
33 |
### Livebook
|
34 |
|
config/config.exs
CHANGED
@@ -7,25 +7,25 @@
|
|
7 |
# General application configuration
|
8 |
import Config
|
9 |
|
10 |
-
config :
|
11 |
-
types:
|
12 |
|
13 |
-
config :
|
14 |
-
ecto_repos: [
|
15 |
generators: [timestamp_type: :utc_datetime]
|
16 |
|
17 |
# Configures the endpoint
|
18 |
-
config :
|
19 |
url: [host: "localhost"],
|
20 |
adapter: Phoenix.Endpoint.Cowboy2Adapter,
|
21 |
render_errors: [
|
22 |
-
formats: [html:
|
23 |
layout: false
|
24 |
],
|
25 |
pubsub_server: :medicode_pubsub,
|
26 |
live_view: [signing_salt: "wVVDe1It"]
|
27 |
|
28 |
-
config :
|
29 |
binary_id: true,
|
30 |
sample_binary_id: "11111111-1111-1111-1111-111111111111"
|
31 |
|
@@ -36,7 +36,7 @@ config :medical_transcription, :generators,
|
|
36 |
#
|
37 |
# For production it's recommended to configure a different adapter
|
38 |
# at the `config/runtime.exs`.
|
39 |
-
config :
|
40 |
|
41 |
# Configure esbuild (the version is required)
|
42 |
config :esbuild,
|
|
|
7 |
# General application configuration
|
8 |
import Config
|
9 |
|
10 |
+
config :medicode, Medicode.Repo,
|
11 |
+
types: Medicode.PostgrexTypes
|
12 |
|
13 |
+
config :medicode,
|
14 |
+
ecto_repos: [Medicode.Repo],
|
15 |
generators: [timestamp_type: :utc_datetime]
|
16 |
|
17 |
# Configures the endpoint
|
18 |
+
config :medicode, MedicodeWeb.Endpoint,
|
19 |
url: [host: "localhost"],
|
20 |
adapter: Phoenix.Endpoint.Cowboy2Adapter,
|
21 |
render_errors: [
|
22 |
+
formats: [html: MedicodeWeb.ErrorHTML, json: MedicodeWeb.ErrorJSON],
|
23 |
layout: false
|
24 |
],
|
25 |
pubsub_server: :medicode_pubsub,
|
26 |
live_view: [signing_salt: "wVVDe1It"]
|
27 |
|
28 |
+
config :medicode, :generators,
|
29 |
binary_id: true,
|
30 |
sample_binary_id: "11111111-1111-1111-1111-111111111111"
|
31 |
|
|
|
36 |
#
|
37 |
# For production it's recommended to configure a different adapter
|
38 |
# at the `config/runtime.exs`.
|
39 |
+
config :medicode, Medicode.Mailer, adapter: Swoosh.Adapters.Local
|
40 |
|
41 |
# Configure esbuild (the version is required)
|
42 |
config :esbuild,
|
config/dev.exs
CHANGED
@@ -1,7 +1,7 @@
|
|
1 |
import Config
|
2 |
|
3 |
# Configure your database
|
4 |
-
config :
|
5 |
hostname: "localhost",
|
6 |
database: "medical_transcription_dev",
|
7 |
stacktrace: true,
|
@@ -14,7 +14,7 @@ config :medical_transcription, MedicalTranscription.Repo,
|
|
14 |
# The watchers configuration can be used to run external
|
15 |
# watchers to your application. For example, we can use it
|
16 |
# to bundle .js and .css sources.
|
17 |
-
config :
|
18 |
# Binding to loopback ipv4 address prevents access from other machines.
|
19 |
# Change to `ip: {0, 0, 0, 0}` to allow access from other machines.
|
20 |
http: [ip: {127, 0, 0, 1}, port: 4000],
|
@@ -52,7 +52,7 @@ config :medical_transcription, MedicalTranscriptionWeb.Endpoint,
|
|
52 |
# different ports.
|
53 |
|
54 |
# Watch static and templates for browser reloading.
|
55 |
-
config :
|
56 |
live_reload: [
|
57 |
patterns: [
|
58 |
~r"priv/static/.*(js|css|png|jpeg|jpg|gif|svg)$",
|
@@ -63,7 +63,7 @@ config :medical_transcription, MedicalTranscriptionWeb.Endpoint,
|
|
63 |
]
|
64 |
|
65 |
# Enable dev routes for dashboard, mailbox, and Phoenix storybook
|
66 |
-
config :
|
67 |
|
68 |
# Do not include metadata nor timestamps in development logs
|
69 |
config :logger, :console, format: "[$level] $message\n", truncate: :infinity
|
|
|
1 |
import Config
|
2 |
|
3 |
# Configure your database
|
4 |
+
config :medicode, Medicode.Repo,
|
5 |
hostname: "localhost",
|
6 |
database: "medical_transcription_dev",
|
7 |
stacktrace: true,
|
|
|
14 |
# The watchers configuration can be used to run external
|
15 |
# watchers to your application. For example, we can use it
|
16 |
# to bundle .js and .css sources.
|
17 |
+
config :medicode, MedicodeWeb.Endpoint,
|
18 |
# Binding to loopback ipv4 address prevents access from other machines.
|
19 |
# Change to `ip: {0, 0, 0, 0}` to allow access from other machines.
|
20 |
http: [ip: {127, 0, 0, 1}, port: 4000],
|
|
|
52 |
# different ports.
|
53 |
|
54 |
# Watch static and templates for browser reloading.
|
55 |
+
config :medicode, MedicodeWeb.Endpoint,
|
56 |
live_reload: [
|
57 |
patterns: [
|
58 |
~r"priv/static/.*(js|css|png|jpeg|jpg|gif|svg)$",
|
|
|
63 |
]
|
64 |
|
65 |
# Enable dev routes for dashboard, mailbox, and Phoenix storybook
|
66 |
+
config :medicode, dev_routes: true
|
67 |
|
68 |
# Do not include metadata nor timestamps in development logs
|
69 |
config :logger, :console, format: "[$level] $message\n", truncate: :infinity
|
config/prod.exs
CHANGED
@@ -5,11 +5,11 @@ import Config
|
|
5 |
# manifest is generated by the `mix assets.deploy` task,
|
6 |
# which you should run after static files are built and
|
7 |
# before starting your production server.
|
8 |
-
config :
|
9 |
cache_static_manifest: "priv/static/cache_manifest.json"
|
10 |
|
11 |
# Configures Swoosh API Client
|
12 |
-
config :swoosh, api_client: Swoosh.ApiClient.Finch, finch_name:
|
13 |
|
14 |
# Disable Swoosh Local Memory Storage
|
15 |
config :swoosh, local: false
|
|
|
5 |
# manifest is generated by the `mix assets.deploy` task,
|
6 |
# which you should run after static files are built and
|
7 |
# before starting your production server.
|
8 |
+
config :medicode, MedicodeWeb.Endpoint,
|
9 |
cache_static_manifest: "priv/static/cache_manifest.json"
|
10 |
|
11 |
# Configures Swoosh API Client
|
12 |
+
config :swoosh, api_client: Swoosh.ApiClient.Finch, finch_name: Medicode.Finch
|
13 |
|
14 |
# Disable Swoosh Local Memory Storage
|
15 |
config :swoosh, local: false
|
config/runtime.exs
CHANGED
@@ -17,7 +17,7 @@ import Config
|
|
17 |
# Alternatively, you can use `mix phx.gen.release` to generate a `bin/server`
|
18 |
# script that automatically sets the env var above.
|
19 |
if System.get_env("PHX_SERVER") do
|
20 |
-
config :
|
21 |
end
|
22 |
|
23 |
if config_env() == :prod do
|
@@ -31,7 +31,7 @@ if config_env() == :prod do
|
|
31 |
# maybe_ipv6 = if System.get_env("ECTO_IPV6") in ~w(true 1), do: [:inet6], else: []
|
32 |
|
33 |
# Previously, this has `socket_options: maybe_ipv6`, but removing that appeared to help connect to the DB.
|
34 |
-
config :
|
35 |
ssl: true,
|
36 |
url: database_url,
|
37 |
pool_size: String.to_integer(System.get_env("POOL_SIZE") || "10"),
|
@@ -60,9 +60,9 @@ if config_env() == :prod do
|
|
60 |
host = System.get_env("PHX_HOST") || "example.com"
|
61 |
port = String.to_integer(System.get_env("PORT") || "4000")
|
62 |
|
63 |
-
config :
|
64 |
|
65 |
-
config :
|
66 |
url: [host: host, port: 443, scheme: "https"],
|
67 |
http: [
|
68 |
# Enable IPv6 and bind on all interfaces.
|
@@ -79,7 +79,7 @@ if config_env() == :prod do
|
|
79 |
# To get SSL working, you will need to add the `https` key
|
80 |
# to your endpoint configuration:
|
81 |
#
|
82 |
-
# config :
|
83 |
# https: [
|
84 |
# ...,
|
85 |
# port: 443,
|
@@ -101,7 +101,7 @@ if config_env() == :prod do
|
|
101 |
# We also recommend setting `force_ssl` in your endpoint, ensuring
|
102 |
# no data is ever sent via http, always redirecting to https:
|
103 |
#
|
104 |
-
# config :
|
105 |
# force_ssl: [hsts: true]
|
106 |
#
|
107 |
# Check `Plug.SSL` for all available options in `force_ssl`.
|
@@ -112,7 +112,7 @@ if config_env() == :prod do
|
|
112 |
# Also, you may need to configure the Swoosh API client of your choice if you
|
113 |
# are not using SMTP. Here is an example of the configuration:
|
114 |
#
|
115 |
-
# config :
|
116 |
# adapter: Swoosh.Adapters.Mailgun,
|
117 |
# api_key: System.get_env("MAILGUN_API_KEY"),
|
118 |
# domain: System.get_env("MAILGUN_DOMAIN")
|
|
|
17 |
# Alternatively, you can use `mix phx.gen.release` to generate a `bin/server`
|
18 |
# script that automatically sets the env var above.
|
19 |
if System.get_env("PHX_SERVER") do
|
20 |
+
config :medicode, MedicodeWeb.Endpoint, server: true
|
21 |
end
|
22 |
|
23 |
if config_env() == :prod do
|
|
|
31 |
# maybe_ipv6 = if System.get_env("ECTO_IPV6") in ~w(true 1), do: [:inet6], else: []
|
32 |
|
33 |
# Previously, this has `socket_options: maybe_ipv6`, but removing that appeared to help connect to the DB.
|
34 |
+
config :medicode, Medicode.Repo,
|
35 |
ssl: true,
|
36 |
url: database_url,
|
37 |
pool_size: String.to_integer(System.get_env("POOL_SIZE") || "10"),
|
|
|
60 |
host = System.get_env("PHX_HOST") || "example.com"
|
61 |
port = String.to_integer(System.get_env("PORT") || "4000")
|
62 |
|
63 |
+
config :medicode, :dns_cluster_query, System.get_env("DNS_CLUSTER_QUERY")
|
64 |
|
65 |
+
config :medicode, MedicodeWeb.Endpoint,
|
66 |
url: [host: host, port: 443, scheme: "https"],
|
67 |
http: [
|
68 |
# Enable IPv6 and bind on all interfaces.
|
|
|
79 |
# To get SSL working, you will need to add the `https` key
|
80 |
# to your endpoint configuration:
|
81 |
#
|
82 |
+
# config :medicode, MedicodeWeb.Endpoint,
|
83 |
# https: [
|
84 |
# ...,
|
85 |
# port: 443,
|
|
|
101 |
# We also recommend setting `force_ssl` in your endpoint, ensuring
|
102 |
# no data is ever sent via http, always redirecting to https:
|
103 |
#
|
104 |
+
# config :medicode, MedicodeWeb.Endpoint,
|
105 |
# force_ssl: [hsts: true]
|
106 |
#
|
107 |
# Check `Plug.SSL` for all available options in `force_ssl`.
|
|
|
112 |
# Also, you may need to configure the Swoosh API client of your choice if you
|
113 |
# are not using SMTP. Here is an example of the configuration:
|
114 |
#
|
115 |
+
# config :medicode, Medicode.Mailer,
|
116 |
# adapter: Swoosh.Adapters.Mailgun,
|
117 |
# api_key: System.get_env("MAILGUN_API_KEY"),
|
118 |
# domain: System.get_env("MAILGUN_DOMAIN")
|
config/test.exs
CHANGED
@@ -9,12 +9,12 @@ config :argon2_elixir, t_cost: 1, m_cost: 8
|
|
9 |
# to provide built-in test partitioning in CI environment.
|
10 |
# Run `mix help test` for more information.
|
11 |
if System.get_env("DATABASE_URL") do
|
12 |
-
config :
|
13 |
url: System.get_env("DATABASE_URL"),
|
14 |
pool: Ecto.Adapters.SQL.Sandbox,
|
15 |
pool_size: 10
|
16 |
else
|
17 |
-
config :
|
18 |
hostname: "localhost",
|
19 |
database: "medical_transcription_test#{System.get_env("MIX_TEST_PARTITION")}",
|
20 |
pool: Ecto.Adapters.SQL.Sandbox,
|
@@ -23,13 +23,13 @@ end
|
|
23 |
|
24 |
# We don't run a server during test. If one is required,
|
25 |
# you can enable the server option below.
|
26 |
-
config :
|
27 |
http: [ip: {127, 0, 0, 1}, port: 4002],
|
28 |
secret_key_base: "s9xRSMfdt6SYbFLJij5dkfi2Nsg7T+CqLJBss2yRy7nlVbZXr1Nd9AsR44meYkBa",
|
29 |
server: false
|
30 |
|
31 |
# In test we don't send emails.
|
32 |
-
config :
|
33 |
|
34 |
# Disable swoosh api client as it is only required for production adapters.
|
35 |
config :swoosh, :api_client, false
|
|
|
9 |
# to provide built-in test partitioning in CI environment.
|
10 |
# Run `mix help test` for more information.
|
11 |
if System.get_env("DATABASE_URL") do
|
12 |
+
config :medicode, Medicode.Repo,
|
13 |
url: System.get_env("DATABASE_URL"),
|
14 |
pool: Ecto.Adapters.SQL.Sandbox,
|
15 |
pool_size: 10
|
16 |
else
|
17 |
+
config :medicode, Medicode.Repo,
|
18 |
hostname: "localhost",
|
19 |
database: "medical_transcription_test#{System.get_env("MIX_TEST_PARTITION")}",
|
20 |
pool: Ecto.Adapters.SQL.Sandbox,
|
|
|
23 |
|
24 |
# We don't run a server during test. If one is required,
|
25 |
# you can enable the server option below.
|
26 |
+
config :medicode, MedicodeWeb.Endpoint,
|
27 |
http: [ip: {127, 0, 0, 1}, port: 4002],
|
28 |
secret_key_base: "s9xRSMfdt6SYbFLJij5dkfi2Nsg7T+CqLJBss2yRy7nlVbZXr1Nd9AsR44meYkBa",
|
29 |
server: false
|
30 |
|
31 |
# In test we don't send emails.
|
32 |
+
config :medicode, Medicode.Mailer, adapter: Swoosh.Adapters.Test
|
33 |
|
34 |
# Disable swoosh api client as it is only required for production adapters.
|
35 |
config :swoosh, :api_client, false
|
lib/medical_transcription.ex
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
-
defmodule
|
2 |
@moduledoc """
|
3 |
-
|
4 |
and business logic.
|
5 |
|
6 |
Contexts are also responsible for managing your data, regardless
|
|
|
1 |
+
defmodule Medicode do
|
2 |
@moduledoc """
|
3 |
+
Medicode keeps the contexts that define your domain
|
4 |
and business logic.
|
5 |
|
6 |
Contexts are also responsible for managing your data, regardless
|
lib/medical_transcription/accounts.ex
CHANGED
@@ -1,12 +1,12 @@
|
|
1 |
-
defmodule
|
2 |
@moduledoc """
|
3 |
The Accounts context.
|
4 |
"""
|
5 |
|
6 |
import Ecto.Query, warn: false
|
7 |
|
8 |
-
alias
|
9 |
-
alias
|
10 |
|
11 |
## Database getters
|
12 |
|
|
|
1 |
+
defmodule Medicode.Accounts do
|
2 |
@moduledoc """
|
3 |
The Accounts context.
|
4 |
"""
|
5 |
|
6 |
import Ecto.Query, warn: false
|
7 |
|
8 |
+
alias Medicode.Accounts.{User, UserNotifier, UserToken}
|
9 |
+
alias Medicode.Repo
|
10 |
|
11 |
## Database getters
|
12 |
|
lib/medical_transcription/accounts/user.ex
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
defmodule
|
2 |
@moduledoc """
|
3 |
User schema
|
4 |
"""
|
@@ -16,8 +16,8 @@ defmodule MedicalTranscription.Accounts.User do
|
|
16 |
field :hashed_password, :string, redact: true
|
17 |
field :confirmed_at, :naive_datetime
|
18 |
|
19 |
-
has_many :transcriptions,
|
20 |
-
has_many :code_feedbacks,
|
21 |
|
22 |
timestamps(type: :utc_datetime)
|
23 |
end
|
@@ -89,7 +89,7 @@ defmodule MedicalTranscription.Accounts.User do
|
|
89 |
defp maybe_validate_unique_email(changeset, opts) do
|
90 |
if Keyword.get(opts, :validate_email, true) do
|
91 |
changeset
|
92 |
-
|> unsafe_validate_unique(:email,
|
93 |
|> unique_constraint(:email)
|
94 |
else
|
95 |
changeset
|
@@ -145,7 +145,7 @@ defmodule MedicalTranscription.Accounts.User do
|
|
145 |
`Argon2.no_user_verify/0` to avoid timing attacks.
|
146 |
"""
|
147 |
def valid_password?(
|
148 |
-
%
|
149 |
password
|
150 |
)
|
151 |
when is_binary(hashed_password) and byte_size(password) > 0 do
|
|
|
1 |
+
defmodule Medicode.Accounts.User do
|
2 |
@moduledoc """
|
3 |
User schema
|
4 |
"""
|
|
|
16 |
field :hashed_password, :string, redact: true
|
17 |
field :confirmed_at, :naive_datetime
|
18 |
|
19 |
+
has_many :transcriptions, Medicode.Transcriptions.Transcription
|
20 |
+
has_many :code_feedbacks, Medicode.Feedback.CodeFeedback
|
21 |
|
22 |
timestamps(type: :utc_datetime)
|
23 |
end
|
|
|
89 |
defp maybe_validate_unique_email(changeset, opts) do
|
90 |
if Keyword.get(opts, :validate_email, true) do
|
91 |
changeset
|
92 |
+
|> unsafe_validate_unique(:email, Medicode.Repo)
|
93 |
|> unique_constraint(:email)
|
94 |
else
|
95 |
changeset
|
|
|
145 |
`Argon2.no_user_verify/0` to avoid timing attacks.
|
146 |
"""
|
147 |
def valid_password?(
|
148 |
+
%Medicode.Accounts.User{hashed_password: hashed_password},
|
149 |
password
|
150 |
)
|
151 |
when is_binary(hashed_password) and byte_size(password) > 0 do
|
lib/medical_transcription/accounts/user_notifier.ex
CHANGED
@@ -1,18 +1,18 @@
|
|
1 |
-
defmodule
|
2 |
@moduledoc """
|
3 |
Email notifications for user management
|
4 |
"""
|
5 |
|
6 |
import Swoosh.Email
|
7 |
|
8 |
-
alias
|
9 |
|
10 |
# Delivers the email using the application mailer.
|
11 |
defp deliver(recipient, subject, body) do
|
12 |
email =
|
13 |
new()
|
14 |
|> to(recipient)
|
15 |
-
|> from({"
|
16 |
|> subject(subject)
|
17 |
|> text_body(body)
|
18 |
|
|
|
1 |
+
defmodule Medicode.Accounts.UserNotifier do
|
2 |
@moduledoc """
|
3 |
Email notifications for user management
|
4 |
"""
|
5 |
|
6 |
import Swoosh.Email
|
7 |
|
8 |
+
alias Medicode.Mailer
|
9 |
|
10 |
# Delivers the email using the application mailer.
|
11 |
defp deliver(recipient, subject, body) do
|
12 |
email =
|
13 |
new()
|
14 |
|> to(recipient)
|
15 |
+
|> from({"Medicode", "contact@example.com"})
|
16 |
|> subject(subject)
|
17 |
|> text_body(body)
|
18 |
|
lib/medical_transcription/accounts/user_token.ex
CHANGED
@@ -1,11 +1,11 @@
|
|
1 |
-
defmodule
|
2 |
@moduledoc """
|
3 |
Token schema
|
4 |
"""
|
5 |
|
6 |
use Ecto.Schema
|
7 |
import Ecto.Query
|
8 |
-
alias
|
9 |
|
10 |
@hash_algorithm :sha256
|
11 |
@rand_size 32
|
@@ -23,7 +23,7 @@ defmodule MedicalTranscription.Accounts.UserToken do
|
|
23 |
field :token, :binary
|
24 |
field :context, :string
|
25 |
field :sent_to, :string
|
26 |
-
belongs_to :user,
|
27 |
|
28 |
timestamps(updated_at: false)
|
29 |
end
|
|
|
1 |
+
defmodule Medicode.Accounts.UserToken do
|
2 |
@moduledoc """
|
3 |
Token schema
|
4 |
"""
|
5 |
|
6 |
use Ecto.Schema
|
7 |
import Ecto.Query
|
8 |
+
alias Medicode.Accounts.UserToken
|
9 |
|
10 |
@hash_algorithm :sha256
|
11 |
@rand_size 32
|
|
|
23 |
field :token, :binary
|
24 |
field :context, :string
|
25 |
field :sent_to, :string
|
26 |
+
belongs_to :user, Medicode.Accounts.User
|
27 |
|
28 |
timestamps(updated_at: false)
|
29 |
end
|
lib/medical_transcription/application.ex
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
defmodule
|
2 |
# See https://hexdocs.pm/elixir/Application.html
|
3 |
# for more information on OTP Applications
|
4 |
@moduledoc false
|
@@ -11,34 +11,34 @@ defmodule MedicalTranscription.Application do
|
|
11 |
@impl true
|
12 |
def start(_type, _args) do
|
13 |
children = [
|
14 |
-
|
15 |
-
|
16 |
{DNSCluster,
|
17 |
-
query: Application.get_env(:
|
18 |
{Phoenix.PubSub, name: :medicode_pubsub},
|
19 |
# Start the Finch HTTP client for sending emails
|
20 |
-
{Finch, name:
|
21 |
transcription_spec(),
|
22 |
token_classification_spec(),
|
23 |
text_embedding_spec(),
|
24 |
{Registry, keys: :unique, name: :transcription_registry},
|
25 |
{
|
26 |
-
|
27 |
strategy: :one_for_one, max_restarts: 1
|
28 |
},
|
29 |
{
|
30 |
-
|
31 |
strategy: :one_for_one, max_restarts: 1
|
32 |
},
|
33 |
-
# Start a worker by calling:
|
34 |
-
# {
|
35 |
# Start to serve requests, typically the last entry
|
36 |
-
|
37 |
]
|
38 |
|
39 |
# See https://hexdocs.pm/elixir/Supervisor.html
|
40 |
# for other strategies and supported options
|
41 |
-
opts = [strategy: :one_for_one, name:
|
42 |
Supervisor.start_link(children, opts)
|
43 |
end
|
44 |
|
@@ -46,19 +46,19 @@ defmodule MedicalTranscription.Application do
|
|
46 |
# whenever the application is updated.
|
47 |
@impl true
|
48 |
def config_change(changed, _new, removed) do
|
49 |
-
|
50 |
:ok
|
51 |
end
|
52 |
|
53 |
defp transcription_spec do
|
54 |
-
Transcriber.child_spec(
|
55 |
end
|
56 |
|
57 |
defp token_classification_spec do
|
58 |
-
KeywordFinder.token_classification_child_spec(
|
59 |
end
|
60 |
|
61 |
defp text_embedding_spec do
|
62 |
-
Vectors.child_spec(
|
63 |
end
|
64 |
end
|
|
|
1 |
+
defmodule Medicode.Application do
|
2 |
# See https://hexdocs.pm/elixir/Application.html
|
3 |
# for more information on OTP Applications
|
4 |
@moduledoc false
|
|
|
11 |
@impl true
|
12 |
def start(_type, _args) do
|
13 |
children = [
|
14 |
+
MedicodeWeb.Telemetry,
|
15 |
+
Medicode.Repo,
|
16 |
{DNSCluster,
|
17 |
+
query: Application.get_env(:medicode, :dns_cluster_query) || :ignore},
|
18 |
{Phoenix.PubSub, name: :medicode_pubsub},
|
19 |
# Start the Finch HTTP client for sending emails
|
20 |
+
{Finch, name: Medicode.Finch},
|
21 |
transcription_spec(),
|
22 |
token_classification_spec(),
|
23 |
text_embedding_spec(),
|
24 |
{Registry, keys: :unique, name: :transcription_registry},
|
25 |
{
|
26 |
+
Medicode.TranscriptionSupervisor,
|
27 |
strategy: :one_for_one, max_restarts: 1
|
28 |
},
|
29 |
{
|
30 |
+
Medicode.ClassificationSupervisor,
|
31 |
strategy: :one_for_one, max_restarts: 1
|
32 |
},
|
33 |
+
# Start a worker by calling: Medicode.Worker.start_link(arg)
|
34 |
+
# {Medicode.Worker, arg},
|
35 |
# Start to serve requests, typically the last entry
|
36 |
+
MedicodeWeb.Endpoint
|
37 |
]
|
38 |
|
39 |
# See https://hexdocs.pm/elixir/Supervisor.html
|
40 |
# for other strategies and supported options
|
41 |
+
opts = [strategy: :one_for_one, name: Medicode.Supervisor]
|
42 |
Supervisor.start_link(children, opts)
|
43 |
end
|
44 |
|
|
|
46 |
# whenever the application is updated.
|
47 |
@impl true
|
48 |
def config_change(changed, _new, removed) do
|
49 |
+
MedicodeWeb.Endpoint.config_change(changed, removed)
|
50 |
:ok
|
51 |
end
|
52 |
|
53 |
defp transcription_spec do
|
54 |
+
Transcriber.child_spec(Medicode.TranscriptionServing, @model_name)
|
55 |
end
|
56 |
|
57 |
defp token_classification_spec do
|
58 |
+
KeywordFinder.token_classification_child_spec(Medicode.TokenClassificationServing)
|
59 |
end
|
60 |
|
61 |
defp text_embedding_spec do
|
62 |
+
Vectors.child_spec(Medicode.TextEmbeddingServing)
|
63 |
end
|
64 |
end
|
lib/medical_transcription/audio/recording_pipeline.ex
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
defmodule
|
2 |
@moduledoc """
|
3 |
Creates a Membrane pipeline for streaming audio from the user's audio input, through the transcription filter, and
|
4 |
to a destination process.
|
@@ -32,7 +32,7 @@ defmodule MedicalTranscription.Audio.RecordingPipeline do
|
|
32 |
# See https://app.asana.com/0/1206407201118057/1206451435774948/f
|
33 |
# defp spec(destination_pid) do
|
34 |
# child(%Membrane.PortAudio.Source{channels: 1, sample_format: :f32le, sample_rate: 16_000})
|
35 |
-
# |> child(
|
36 |
# |> child(%Membrane.Debug.Sink{
|
37 |
# handle_buffer: fn buffer ->
|
38 |
# send(destination_pid, {:received_audio_payload, buffer.payload})
|
|
|
1 |
+
defmodule Medicode.Audio.RecordingPipeline do
|
2 |
@moduledoc """
|
3 |
Creates a Membrane pipeline for streaming audio from the user's audio input, through the transcription filter, and
|
4 |
to a destination process.
|
|
|
32 |
# See https://app.asana.com/0/1206407201118057/1206451435774948/f
|
33 |
# defp spec(destination_pid) do
|
34 |
# child(%Membrane.PortAudio.Source{channels: 1, sample_format: :f32le, sample_rate: 16_000})
|
35 |
+
# |> child(Medicode.Audio.TranscriptionFilter)
|
36 |
# |> child(%Membrane.Debug.Sink{
|
37 |
# handle_buffer: fn buffer ->
|
38 |
# send(destination_pid, {:received_audio_payload, buffer.payload})
|
lib/medical_transcription/audio/transcription_filter.ex
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
defmodule
|
2 |
@moduledoc """
|
3 |
A Membrane filter to transcribe audio with the Whisper model.
|
4 |
|
@@ -87,7 +87,7 @@ defmodule MedicalTranscription.Audio.TranscriptionFilter do
|
|
87 |
# and prepend it to the subsequent chunk.
|
88 |
speech =
|
89 |
if state.speech == <<>> do
|
90 |
-
|
91 |
else
|
92 |
state.speech <> data
|
93 |
end
|
@@ -98,7 +98,7 @@ defmodule MedicalTranscription.Audio.TranscriptionFilter do
|
|
98 |
model_input = Nx.from_binary(speech, :f32)
|
99 |
|
100 |
result =
|
101 |
-
Nx.Serving.batched_run(
|
102 |
|> Enum.into([])
|
103 |
|
104 |
transcription = Enum.map_join(result, & &1.text)
|
|
|
1 |
+
defmodule Medicode.Audio.TranscriptionFilter do
|
2 |
@moduledoc """
|
3 |
A Membrane filter to transcribe audio with the Whisper model.
|
4 |
|
|
|
87 |
# and prepend it to the subsequent chunk.
|
88 |
speech =
|
89 |
if state.speech == <<>> do
|
90 |
+
Medicode.Audio.Utilities.filter_silence(data, state)
|
91 |
else
|
92 |
state.speech <> data
|
93 |
end
|
|
|
98 |
model_input = Nx.from_binary(speech, :f32)
|
99 |
|
100 |
result =
|
101 |
+
Nx.Serving.batched_run(Medicode.TranscriptionServing, model_input)
|
102 |
|> Enum.into([])
|
103 |
|
104 |
transcription = Enum.map_join(result, & &1.text)
|
lib/medical_transcription/audio/utilities.ex
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
defmodule
|
2 |
@moduledoc """
|
3 |
Holds audio processing utility functions.
|
4 |
"""
|
|
|
1 |
+
defmodule Medicode.Audio.Utilities do
|
2 |
@moduledoc """
|
3 |
Holds audio processing utility functions.
|
4 |
"""
|
lib/medical_transcription/classification_server.ex
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
defmodule
|
2 |
@moduledoc """
|
3 |
GenServer responsible for classifying transcription text
|
4 |
"""
|
@@ -6,9 +6,9 @@ defmodule MedicalTranscription.ClassificationServer do
|
|
6 |
|
7 |
alias AudioTagger.KeywordFinder
|
8 |
|
9 |
-
alias
|
10 |
-
alias
|
11 |
-
alias
|
12 |
|
13 |
@registry :transcription_registry
|
14 |
|
@@ -89,7 +89,7 @@ defmodule MedicalTranscription.ClassificationServer do
|
|
89 |
|
90 |
defp find_keywords(chunk) do
|
91 |
%{entities: entities} =
|
92 |
-
Nx.Serving.batched_run(
|
93 |
|
94 |
phrases = KeywordFinder.cleanup_phrases(entities)
|
95 |
|
|
|
1 |
+
defmodule Medicode.ClassificationServer do
|
2 |
@moduledoc """
|
3 |
GenServer responsible for classifying transcription text
|
4 |
"""
|
|
|
6 |
|
7 |
alias AudioTagger.KeywordFinder
|
8 |
|
9 |
+
alias Medicode.Coding
|
10 |
+
alias Medicode.Coding.CodeVectorMatch
|
11 |
+
alias Medicode.Transcriptions
|
12 |
|
13 |
@registry :transcription_registry
|
14 |
|
|
|
89 |
|
90 |
defp find_keywords(chunk) do
|
91 |
%{entities: entities} =
|
92 |
+
Nx.Serving.batched_run(Medicode.TokenClassificationServing, chunk.text)
|
93 |
|
94 |
phrases = KeywordFinder.cleanup_phrases(entities)
|
95 |
|
lib/medical_transcription/classification_supervisor.ex
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
defmodule
|
2 |
# Automatically defines child_spec/1
|
3 |
use DynamicSupervisor
|
4 |
|
@@ -14,7 +14,7 @@ defmodule MedicalTranscription.ClassificationSupervisor do
|
|
14 |
def start_classification(transcription_chunk) do
|
15 |
# Shorthand to retrieve the child specification from the `child_spec/1` method of the given module.
|
16 |
spec = {
|
17 |
-
|
18 |
%{chunk: transcription_chunk, name: "transcription_chunk:#{transcription_chunk.id}"}
|
19 |
}
|
20 |
|
|
|
1 |
+
defmodule Medicode.ClassificationSupervisor do
|
2 |
# Automatically defines child_spec/1
|
3 |
use DynamicSupervisor
|
4 |
|
|
|
14 |
def start_classification(transcription_chunk) do
|
15 |
# Shorthand to retrieve the child specification from the `child_spec/1` method of the given module.
|
16 |
spec = {
|
17 |
+
Medicode.ClassificationServer,
|
18 |
%{chunk: transcription_chunk, name: "transcription_chunk:#{transcription_chunk.id}"}
|
19 |
}
|
20 |
|
lib/medical_transcription/coding.ex
CHANGED
@@ -1,15 +1,15 @@
|
|
1 |
-
defmodule
|
2 |
@moduledoc """
|
3 |
Takes a portion of text and searches for closely matching results within a list of vectors, by using AudioTagger's
|
4 |
SemanticSearch module.
|
5 |
"""
|
6 |
|
7 |
-
alias
|
8 |
import Ecto.Query
|
9 |
import Pgvector.Ecto.Query
|
10 |
|
11 |
-
alias
|
12 |
-
alias
|
13 |
|
14 |
def insert_vector(params) do
|
15 |
changeset = CodeVector.changeset(%CodeVector{}, params)
|
@@ -73,7 +73,7 @@ defmodule MedicalTranscription.Coding do
|
|
73 |
search_vector_for_db = compute_vector_as_list(text)
|
74 |
|
75 |
past_feedbacks =
|
76 |
-
|
77 |
|
78 |
code_vectors = find_similar(search_vector_for_db, k)
|
79 |
code_vectors_for_feedback = find_for_feedback(search_vector_for_db, past_feedbacks)
|
@@ -88,7 +88,7 @@ defmodule MedicalTranscription.Coding do
|
|
88 |
|
89 |
@doc "Creates a vector embedding for text using the text embedding serving in the application's supervision tree."
|
90 |
def compute_vector_as_list(text) do
|
91 |
-
|
92 |
|> Nx.Serving.batched_run(text)
|
93 |
|> Map.get(:embedding)
|
94 |
|> Nx.to_flat_list()
|
|
|
1 |
+
defmodule Medicode.Coding do
|
2 |
@moduledoc """
|
3 |
Takes a portion of text and searches for closely matching results within a list of vectors, by using AudioTagger's
|
4 |
SemanticSearch module.
|
5 |
"""
|
6 |
|
7 |
+
alias Medicode.Repo
|
8 |
import Ecto.Query
|
9 |
import Pgvector.Ecto.Query
|
10 |
|
11 |
+
alias Medicode.Coding.{CodeVector, CodeVectorMatch}
|
12 |
+
alias Medicode.Transcriptions.TranscriptionChunk
|
13 |
|
14 |
def insert_vector(params) do
|
15 |
changeset = CodeVector.changeset(%CodeVector{}, params)
|
|
|
73 |
search_vector_for_db = compute_vector_as_list(text)
|
74 |
|
75 |
past_feedbacks =
|
76 |
+
Medicode.Feedback.find_related_feedback(search_vector_for_db, opts)
|
77 |
|
78 |
code_vectors = find_similar(search_vector_for_db, k)
|
79 |
code_vectors_for_feedback = find_for_feedback(search_vector_for_db, past_feedbacks)
|
|
|
88 |
|
89 |
@doc "Creates a vector embedding for text using the text embedding serving in the application's supervision tree."
|
90 |
def compute_vector_as_list(text) do
|
91 |
+
Medicode.TextEmbeddingServing
|
92 |
|> Nx.Serving.batched_run(text)
|
93 |
|> Map.get(:embedding)
|
94 |
|> Nx.to_flat_list()
|
lib/medical_transcription/coding/code_vector.ex
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
defmodule
|
2 |
@moduledoc """
|
3 |
Represents a code and its description, along with a vector embedding for its description.
|
4 |
"""
|
@@ -13,7 +13,7 @@ defmodule MedicalTranscription.Coding.CodeVector do
|
|
13 |
field :description_vector, Pgvector.Ecto.Vector, redact: true
|
14 |
|
15 |
has_many :transcription_chunk_code_vectors,
|
16 |
-
|
17 |
|
18 |
has_many :transcription_chunks,
|
19 |
through: [:transcription_chunk_code_vectors, :transcription_chunk]
|
|
|
1 |
+
defmodule Medicode.Coding.CodeVector do
|
2 |
@moduledoc """
|
3 |
Represents a code and its description, along with a vector embedding for its description.
|
4 |
"""
|
|
|
13 |
field :description_vector, Pgvector.Ecto.Vector, redact: true
|
14 |
|
15 |
has_many :transcription_chunk_code_vectors,
|
16 |
+
Medicode.Transcriptions.TranscriptionChunkCodeVector
|
17 |
|
18 |
has_many :transcription_chunks,
|
19 |
through: [:transcription_chunk_code_vectors, :transcription_chunk]
|
lib/medical_transcription/coding/code_vector_match.ex
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
defmodule
|
2 |
@moduledoc """
|
3 |
Represents a vector match found in the database, along with its similarity score.
|
4 |
"""
|
|
|
1 |
+
defmodule Medicode.Coding.CodeVectorMatch do
|
2 |
@moduledoc """
|
3 |
Represents a vector match found in the database, along with its similarity score.
|
4 |
"""
|
lib/medical_transcription/coding/vector_precomputation.ex
CHANGED
@@ -1,7 +1,7 @@
|
|
1 |
-
defmodule
|
2 |
@moduledoc "Populate database with vector embeddings from downloaded ICD-9 code list"
|
3 |
|
4 |
-
alias
|
5 |
|
6 |
@doc "Downloads the ICD-9 codelist, calculates vector embeddings for each, and adds them to the database"
|
7 |
def run do
|
|
|
1 |
+
defmodule Medicode.Coding.VectorPrecomputation do
|
2 |
@moduledoc "Populate database with vector embeddings from downloaded ICD-9 code list"
|
3 |
|
4 |
+
alias Medicode.Coding
|
5 |
|
6 |
@doc "Downloads the ICD-9 codelist, calculates vector embeddings for each, and adds them to the database"
|
7 |
def run do
|
lib/medical_transcription/feedback.ex
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
defmodule
|
2 |
@moduledoc """
|
3 |
Context for managing user feedback on transcribed text and codes.
|
4 |
"""
|
@@ -6,8 +6,8 @@ defmodule MedicalTranscription.Feedback do
|
|
6 |
import Pgvector.Ecto.Query, only: [cosine_distance: 2]
|
7 |
|
8 |
alias Ecto.Multi
|
9 |
-
alias
|
10 |
-
alias
|
11 |
|
12 |
def track_response(params) do
|
13 |
# TODO: Move this changeset into Multi and read errors from the result within the case statement
|
|
|
1 |
+
defmodule Medicode.Feedback do
|
2 |
@moduledoc """
|
3 |
Context for managing user feedback on transcribed text and codes.
|
4 |
"""
|
|
|
6 |
import Pgvector.Ecto.Query, only: [cosine_distance: 2]
|
7 |
|
8 |
alias Ecto.Multi
|
9 |
+
alias Medicode.Feedback.CodeFeedback
|
10 |
+
alias Medicode.Repo
|
11 |
|
12 |
def track_response(params) do
|
13 |
# TODO: Move this changeset into Multi and read errors from the result within the case statement
|
lib/medical_transcription/feedback/code_feedback.ex
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
defmodule
|
2 |
@moduledoc """
|
3 |
Represents a user's feedback on a given code classification.
|
4 |
"""
|
@@ -11,8 +11,8 @@ defmodule MedicalTranscription.Feedback.CodeFeedback do
|
|
11 |
field(:text_vector, Pgvector.Ecto.Vector)
|
12 |
field(:response, :boolean)
|
13 |
|
14 |
-
belongs_to(:code_vector,
|
15 |
-
belongs_to(:user,
|
16 |
|
17 |
timestamps(type: :utc_datetime)
|
18 |
end
|
|
|
1 |
+
defmodule Medicode.Feedback.CodeFeedback do
|
2 |
@moduledoc """
|
3 |
Represents a user's feedback on a given code classification.
|
4 |
"""
|
|
|
11 |
field(:text_vector, Pgvector.Ecto.Vector)
|
12 |
field(:response, :boolean)
|
13 |
|
14 |
+
belongs_to(:code_vector, Medicode.Coding.CodeVector)
|
15 |
+
belongs_to(:user, Medicode.Accounts.User)
|
16 |
|
17 |
timestamps(type: :utc_datetime)
|
18 |
end
|
lib/medical_transcription/mailer.ex
CHANGED
@@ -1,3 +1,3 @@
|
|
1 |
-
defmodule
|
2 |
-
use Swoosh.Mailer, otp_app: :
|
3 |
end
|
|
|
1 |
+
defmodule Medicode.Mailer do
|
2 |
+
use Swoosh.Mailer, otp_app: :medicode
|
3 |
end
|
lib/medical_transcription/release.ex
CHANGED
@@ -1,9 +1,9 @@
|
|
1 |
-
defmodule
|
2 |
@moduledoc """
|
3 |
Used for executing DB release tasks when run in production without Mix
|
4 |
installed.
|
5 |
"""
|
6 |
-
@app :
|
7 |
|
8 |
def migrate do
|
9 |
load_app()
|
@@ -20,9 +20,9 @@ defmodule MedicalTranscription.Release do
|
|
20 |
|
21 |
def precompute_code_vectors do
|
22 |
load_app()
|
23 |
-
Application.ensure_all_started(:
|
24 |
|
25 |
-
|
26 |
end
|
27 |
|
28 |
defp repos do
|
|
|
1 |
+
defmodule Medicode.Release do
|
2 |
@moduledoc """
|
3 |
Used for executing DB release tasks when run in production without Mix
|
4 |
installed.
|
5 |
"""
|
6 |
+
@app :medicode
|
7 |
|
8 |
def migrate do
|
9 |
load_app()
|
|
|
20 |
|
21 |
def precompute_code_vectors do
|
22 |
load_app()
|
23 |
+
Application.ensure_all_started(:medicode)
|
24 |
|
25 |
+
Medicode.Coding.VectorPrecomputation.run()
|
26 |
end
|
27 |
|
28 |
defp repos do
|
lib/medical_transcription/repo.ex
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
-
defmodule
|
2 |
use Ecto.Repo,
|
3 |
-
otp_app: :
|
4 |
adapter: Ecto.Adapters.Postgres
|
5 |
|
6 |
def collect_errors(changeset) do
|
|
|
1 |
+
defmodule Medicode.Repo do
|
2 |
use Ecto.Repo,
|
3 |
+
otp_app: :medicode,
|
4 |
adapter: Ecto.Adapters.Postgres
|
5 |
|
6 |
def collect_errors(changeset) do
|
lib/medical_transcription/transcription_server.ex
CHANGED
@@ -1,11 +1,11 @@
|
|
1 |
-
defmodule
|
2 |
@moduledoc """
|
3 |
GenServer responsible for transcribing audio files
|
4 |
"""
|
5 |
use GenServer
|
6 |
|
7 |
-
alias
|
8 |
-
alias
|
9 |
|
10 |
@registry :transcription_registry
|
11 |
|
@@ -72,7 +72,7 @@ defmodule MedicalTranscription.TranscriptionServer do
|
|
72 |
{:transcription_updated, chunk}
|
73 |
)
|
74 |
|
75 |
-
|
76 |
|
77 |
{:noreply, state}
|
78 |
end
|
@@ -111,7 +111,7 @@ defmodule MedicalTranscription.TranscriptionServer do
|
|
111 |
defp stream_transcription_and_search(audio_file_path) do
|
112 |
# audio transcription + semantic search
|
113 |
summary_text =
|
114 |
-
|
115 |
|> Nx.Serving.batched_run({:file, audio_file_path})
|
116 |
|> Stream.with_index()
|
117 |
|> Stream.map(fn {chunk, index} ->
|
|
|
1 |
+
defmodule Medicode.TranscriptionServer do
|
2 |
@moduledoc """
|
3 |
GenServer responsible for transcribing audio files
|
4 |
"""
|
5 |
use GenServer
|
6 |
|
7 |
+
alias Medicode.Transcriptions
|
8 |
+
alias Medicode.Transcriptions.Transcription
|
9 |
|
10 |
@registry :transcription_registry
|
11 |
|
|
|
72 |
{:transcription_updated, chunk}
|
73 |
)
|
74 |
|
75 |
+
Medicode.ClassificationSupervisor.start_classification(chunk)
|
76 |
|
77 |
{:noreply, state}
|
78 |
end
|
|
|
111 |
defp stream_transcription_and_search(audio_file_path) do
|
112 |
# audio transcription + semantic search
|
113 |
summary_text =
|
114 |
+
Medicode.TranscriptionServing
|
115 |
|> Nx.Serving.batched_run({:file, audio_file_path})
|
116 |
|> Stream.with_index()
|
117 |
|> Stream.map(fn {chunk, index} ->
|
lib/medical_transcription/transcription_supervisor.ex
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
defmodule
|
2 |
# Automatically defines child_spec/1
|
3 |
use DynamicSupervisor
|
4 |
|
@@ -13,7 +13,7 @@ defmodule MedicalTranscription.TranscriptionSupervisor do
|
|
13 |
|
14 |
def start_transcription(transcription) do
|
15 |
spec = {
|
16 |
-
|
17 |
%{transcription: transcription, name: "transcription:#{transcription.id}"}
|
18 |
}
|
19 |
|
|
|
1 |
+
defmodule Medicode.TranscriptionSupervisor do
|
2 |
# Automatically defines child_spec/1
|
3 |
use DynamicSupervisor
|
4 |
|
|
|
13 |
|
14 |
def start_transcription(transcription) do
|
15 |
spec = {
|
16 |
+
Medicode.TranscriptionServer,
|
17 |
%{transcription: transcription, name: "transcription:#{transcription.id}"}
|
18 |
}
|
19 |
|
lib/medical_transcription/transcriptions.ex
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
defmodule
|
2 |
@moduledoc """
|
3 |
The Transcriptions context.
|
4 |
"""
|
@@ -7,9 +7,9 @@ defmodule MedicalTranscription.Transcriptions do
|
|
7 |
|
8 |
alias Ecto.Changeset
|
9 |
|
10 |
-
alias
|
11 |
|
12 |
-
alias
|
13 |
Transcription,
|
14 |
TranscriptionChunk,
|
15 |
TranscriptionChunkKeyword,
|
@@ -25,7 +25,7 @@ defmodule MedicalTranscription.Transcriptions do
|
|
25 |
%Transcription{audio_file: "my-audio.mp3"}
|
26 |
"""
|
27 |
def transcribe_audio(transcription) do
|
28 |
-
|
29 |
end
|
30 |
|
31 |
@doc """
|
@@ -162,8 +162,8 @@ defmodule MedicalTranscription.Transcriptions do
|
|
162 |
v.assigned_by_user_id == ^assigned_by_user_id
|
163 |
)
|
164 |
|
165 |
-
with %TranscriptionChunkCodeVector{} = chunk_vector <-
|
166 |
-
{:ok, _struct} <-
|
167 |
{:ok, chunk_vector}
|
168 |
else
|
169 |
res -> res
|
|
|
1 |
+
defmodule Medicode.Transcriptions do
|
2 |
@moduledoc """
|
3 |
The Transcriptions context.
|
4 |
"""
|
|
|
7 |
|
8 |
alias Ecto.Changeset
|
9 |
|
10 |
+
alias Medicode.Repo
|
11 |
|
12 |
+
alias Medicode.Transcriptions.{
|
13 |
Transcription,
|
14 |
TranscriptionChunk,
|
15 |
TranscriptionChunkKeyword,
|
|
|
25 |
%Transcription{audio_file: "my-audio.mp3"}
|
26 |
"""
|
27 |
def transcribe_audio(transcription) do
|
28 |
+
Medicode.TranscriptionSupervisor.start_transcription(transcription)
|
29 |
end
|
30 |
|
31 |
@doc """
|
|
|
162 |
v.assigned_by_user_id == ^assigned_by_user_id
|
163 |
)
|
164 |
|
165 |
+
with %TranscriptionChunkCodeVector{} = chunk_vector <- Medicode.Repo.one(query),
|
166 |
+
{:ok, _struct} <- Medicode.Repo.delete(chunk_vector) do
|
167 |
{:ok, chunk_vector}
|
168 |
else
|
169 |
res -> res
|
lib/medical_transcription/transcriptions/transcription.ex
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
defmodule
|
2 |
@moduledoc """
|
3 |
Transcription schema
|
4 |
"""
|
@@ -15,9 +15,9 @@ defmodule MedicalTranscription.Transcriptions.Transcription do
|
|
15 |
field :status, Ecto.Enum,
|
16 |
values: [new: 0, waiting: 1, transcribing: 2, finished: 3, failed: 4]
|
17 |
|
18 |
-
belongs_to :user,
|
19 |
|
20 |
-
has_many :chunks,
|
21 |
|
22 |
timestamps(type: :utc_datetime)
|
23 |
end
|
|
|
1 |
+
defmodule Medicode.Transcriptions.Transcription do
|
2 |
@moduledoc """
|
3 |
Transcription schema
|
4 |
"""
|
|
|
15 |
field :status, Ecto.Enum,
|
16 |
values: [new: 0, waiting: 1, transcribing: 2, finished: 3, failed: 4]
|
17 |
|
18 |
+
belongs_to :user, Medicode.Accounts.User
|
19 |
|
20 |
+
has_many :chunks, Medicode.Transcriptions.TranscriptionChunk
|
21 |
|
22 |
timestamps(type: :utc_datetime)
|
23 |
end
|
lib/medical_transcription/transcriptions/transcription_chunk.ex
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
defmodule
|
2 |
use Ecto.Schema
|
3 |
import Ecto.Changeset
|
4 |
|
@@ -9,13 +9,13 @@ defmodule MedicalTranscription.Transcriptions.TranscriptionChunk do
|
|
9 |
field(:start_mark, :string)
|
10 |
field(:end_mark, :string)
|
11 |
|
12 |
-
belongs_to(:transcription,
|
13 |
|
14 |
-
has_many(:keywords,
|
15 |
|
16 |
has_many(
|
17 |
:transcription_chunk_code_vectors,
|
18 |
-
|
19 |
)
|
20 |
|
21 |
has_many(:code_vectors,
|
@@ -23,7 +23,7 @@ defmodule MedicalTranscription.Transcriptions.TranscriptionChunk do
|
|
23 |
preload_order: [desc: :cosine_similarity]
|
24 |
)
|
25 |
|
26 |
-
has_many(:code_feedbacks,
|
27 |
|
28 |
timestamps(type: :utc_datetime)
|
29 |
end
|
|
|
1 |
+
defmodule Medicode.Transcriptions.TranscriptionChunk do
|
2 |
use Ecto.Schema
|
3 |
import Ecto.Changeset
|
4 |
|
|
|
9 |
field(:start_mark, :string)
|
10 |
field(:end_mark, :string)
|
11 |
|
12 |
+
belongs_to(:transcription, Medicode.Transcriptions.Transcription)
|
13 |
|
14 |
+
has_many(:keywords, Medicode.Transcriptions.TranscriptionChunkKeyword)
|
15 |
|
16 |
has_many(
|
17 |
:transcription_chunk_code_vectors,
|
18 |
+
Medicode.Transcriptions.TranscriptionChunkCodeVector
|
19 |
)
|
20 |
|
21 |
has_many(:code_vectors,
|
|
|
23 |
preload_order: [desc: :cosine_similarity]
|
24 |
)
|
25 |
|
26 |
+
has_many(:code_feedbacks, Medicode.Feedback.CodeFeedback)
|
27 |
|
28 |
timestamps(type: :utc_datetime)
|
29 |
end
|
lib/medical_transcription/transcriptions/transcription_chunk_code_vector.ex
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
defmodule
|
2 |
use Ecto.Schema
|
3 |
import Ecto.Changeset
|
4 |
|
@@ -8,9 +8,9 @@ defmodule MedicalTranscription.Transcriptions.TranscriptionChunkCodeVector do
|
|
8 |
field :cosine_similarity, :float
|
9 |
field :weighting, {:array, :string}
|
10 |
|
11 |
-
belongs_to :transcription_chunk,
|
12 |
-
belongs_to :code_vector,
|
13 |
-
belongs_to :assigned_by_user,
|
14 |
|
15 |
timestamps(type: :utc_datetime)
|
16 |
end
|
|
|
1 |
+
defmodule Medicode.Transcriptions.TranscriptionChunkCodeVector do
|
2 |
use Ecto.Schema
|
3 |
import Ecto.Changeset
|
4 |
|
|
|
8 |
field :cosine_similarity, :float
|
9 |
field :weighting, {:array, :string}
|
10 |
|
11 |
+
belongs_to :transcription_chunk, Medicode.Transcriptions.TranscriptionChunk
|
12 |
+
belongs_to :code_vector, Medicode.Coding.CodeVector
|
13 |
+
belongs_to :assigned_by_user, Medicode.Accounts.User
|
14 |
|
15 |
timestamps(type: :utc_datetime)
|
16 |
end
|
lib/medical_transcription/transcriptions/transcription_chunk_keyword.ex
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
defmodule
|
2 |
use Ecto.Schema
|
3 |
import Ecto.Changeset
|
4 |
|
@@ -8,7 +8,7 @@ defmodule MedicalTranscription.Transcriptions.TranscriptionChunkKeyword do
|
|
8 |
field :keyword, :string
|
9 |
field :score, :float
|
10 |
|
11 |
-
belongs_to :transcription_chunk,
|
12 |
|
13 |
timestamps(type: :utc_datetime)
|
14 |
end
|
|
|
1 |
+
defmodule Medicode.Transcriptions.TranscriptionChunkKeyword do
|
2 |
use Ecto.Schema
|
3 |
import Ecto.Changeset
|
4 |
|
|
|
8 |
field :keyword, :string
|
9 |
field :score, :float
|
10 |
|
11 |
+
belongs_to :transcription_chunk, Medicode.Transcriptions.TranscriptionChunk
|
12 |
|
13 |
timestamps(type: :utc_datetime)
|
14 |
end
|
lib/medical_transcription/utilities.ex
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
defmodule
|
2 |
@moduledoc """
|
3 |
Contains general utility functions that are not specific to a domain or context in the system.
|
4 |
"""
|
|
|
1 |
+
defmodule Medicode.Utilities do
|
2 |
@moduledoc """
|
3 |
Contains general utility functions that are not specific to a domain or context in the system.
|
4 |
"""
|
lib/medical_transcription_web.ex
CHANGED
@@ -1,12 +1,12 @@
|
|
1 |
-
defmodule
|
2 |
@moduledoc """
|
3 |
The entrypoint for defining your web interface, such
|
4 |
as controllers, components, channels, and so on.
|
5 |
|
6 |
This can be used in your application as:
|
7 |
|
8 |
-
use
|
9 |
-
use
|
10 |
|
11 |
The definitions below will be executed for every controller,
|
12 |
component, etc, so keep them short and clean, focused
|
@@ -40,10 +40,10 @@ defmodule MedicalTranscriptionWeb do
|
|
40 |
quote do
|
41 |
use Phoenix.Controller,
|
42 |
formats: [:html, :json],
|
43 |
-
layouts: [html:
|
44 |
|
45 |
import Plug.Conn
|
46 |
-
import
|
47 |
|
48 |
unquote(verified_routes())
|
49 |
end
|
@@ -52,7 +52,7 @@ defmodule MedicalTranscriptionWeb do
|
|
52 |
def live_view do
|
53 |
quote do
|
54 |
use Phoenix.LiveView,
|
55 |
-
layout: {
|
56 |
|
57 |
unquote(html_helpers())
|
58 |
end
|
@@ -84,9 +84,9 @@ defmodule MedicalTranscriptionWeb do
|
|
84 |
# HTML escaping functionality
|
85 |
import Phoenix.HTML
|
86 |
# Core UI components and translation
|
87 |
-
import
|
88 |
-
import
|
89 |
-
import
|
90 |
|
91 |
# Shortcut for generating JS commands
|
92 |
alias Phoenix.LiveView.JS
|
@@ -99,9 +99,9 @@ defmodule MedicalTranscriptionWeb do
|
|
99 |
def verified_routes do
|
100 |
quote do
|
101 |
use Phoenix.VerifiedRoutes,
|
102 |
-
endpoint:
|
103 |
-
router:
|
104 |
-
statics:
|
105 |
end
|
106 |
end
|
107 |
|
|
|
1 |
+
defmodule MedicodeWeb do
|
2 |
@moduledoc """
|
3 |
The entrypoint for defining your web interface, such
|
4 |
as controllers, components, channels, and so on.
|
5 |
|
6 |
This can be used in your application as:
|
7 |
|
8 |
+
use MedicodeWeb, :controller
|
9 |
+
use MedicodeWeb, :html
|
10 |
|
11 |
The definitions below will be executed for every controller,
|
12 |
component, etc, so keep them short and clean, focused
|
|
|
40 |
quote do
|
41 |
use Phoenix.Controller,
|
42 |
formats: [:html, :json],
|
43 |
+
layouts: [html: MedicodeWeb.Layouts]
|
44 |
|
45 |
import Plug.Conn
|
46 |
+
import MedicodeWeb.Gettext
|
47 |
|
48 |
unquote(verified_routes())
|
49 |
end
|
|
|
52 |
def live_view do
|
53 |
quote do
|
54 |
use Phoenix.LiveView,
|
55 |
+
layout: {MedicodeWeb.Layouts, :app}
|
56 |
|
57 |
unquote(html_helpers())
|
58 |
end
|
|
|
84 |
# HTML escaping functionality
|
85 |
import Phoenix.HTML
|
86 |
# Core UI components and translation
|
87 |
+
import MedicodeWeb.CoreComponents
|
88 |
+
import MedicodeWeb.Components
|
89 |
+
import MedicodeWeb.Gettext
|
90 |
|
91 |
# Shortcut for generating JS commands
|
92 |
alias Phoenix.LiveView.JS
|
|
|
99 |
def verified_routes do
|
100 |
quote do
|
101 |
use Phoenix.VerifiedRoutes,
|
102 |
+
endpoint: MedicodeWeb.Endpoint,
|
103 |
+
router: MedicodeWeb.Router,
|
104 |
+
statics: MedicodeWeb.static_paths()
|
105 |
end
|
106 |
end
|
107 |
|
lib/medical_transcription_web/components/code_select.ex
CHANGED
@@ -1,14 +1,14 @@
|
|
1 |
-
defmodule
|
2 |
@moduledoc """
|
3 |
An auto-complete select field that allows users to search for codes to add by either `code` or `description`.
|
4 |
"""
|
5 |
|
6 |
-
use
|
7 |
|
8 |
-
import
|
9 |
|
10 |
-
alias
|
11 |
-
alias
|
12 |
|
13 |
@impl Phoenix.LiveComponent
|
14 |
def mount(socket) do
|
@@ -90,7 +90,7 @@ defmodule MedicalTranscriptionWeb.Components.CodeSelect do
|
|
90 |
def handle_event("suggest-code", params, socket) do
|
91 |
form_id = socket.assigns.form.id
|
92 |
%{^form_id => %{"search_term" => value}} = params
|
93 |
-
suggested_codes =
|
94 |
|
95 |
{:noreply, assign(socket, :codes, suggested_codes)}
|
96 |
end
|
@@ -101,7 +101,7 @@ defmodule MedicalTranscriptionWeb.Components.CodeSelect do
|
|
101 |
|
102 |
def handle_event("choose-code", %{"code" => code}, socket) do
|
103 |
selected_code = Enum.find(socket.assigns.codes, &(&1.code == code))
|
104 |
-
text_vector =
|
105 |
|
106 |
code_feedback =
|
107 |
Feedback.insert_and_return(%{
|
@@ -118,13 +118,13 @@ defmodule MedicalTranscriptionWeb.Components.CodeSelect do
|
|
118 |
cosine_similarity: 1.0
|
119 |
})
|
120 |
|
121 |
-
send_update(
|
122 |
id: "transcription-chunk-#{socket.assigns.chunk.id}",
|
123 |
chunk: socket.assigns.chunk,
|
124 |
chunk_id: socket.assigns.chunk.id,
|
125 |
current_user: socket.assigns.current_user,
|
126 |
-
on_feedback: &
|
127 |
-
on_remove_code: &
|
128 |
)
|
129 |
|
130 |
Phoenix.PubSub.broadcast(
|
|
|
1 |
+
defmodule MedicodeWeb.Components.CodeSelect do
|
2 |
@moduledoc """
|
3 |
An auto-complete select field that allows users to search for codes to add by either `code` or `description`.
|
4 |
"""
|
5 |
|
6 |
+
use MedicodeWeb, :live_component
|
7 |
|
8 |
+
import MedicodeWeb.Components, only: [code_display: 1]
|
9 |
|
10 |
+
alias Medicode.Feedback
|
11 |
+
alias Medicode.Transcriptions
|
12 |
|
13 |
@impl Phoenix.LiveComponent
|
14 |
def mount(socket) do
|
|
|
90 |
def handle_event("suggest-code", params, socket) do
|
91 |
form_id = socket.assigns.form.id
|
92 |
%{^form_id => %{"search_term" => value}} = params
|
93 |
+
suggested_codes = Medicode.Coding.search_for_code_vector(value)
|
94 |
|
95 |
{:noreply, assign(socket, :codes, suggested_codes)}
|
96 |
end
|
|
|
101 |
|
102 |
def handle_event("choose-code", %{"code" => code}, socket) do
|
103 |
selected_code = Enum.find(socket.assigns.codes, &(&1.code == code))
|
104 |
+
text_vector = Medicode.Coding.compute_vector_as_list(socket.assigns.text)
|
105 |
|
106 |
code_feedback =
|
107 |
Feedback.insert_and_return(%{
|
|
|
118 |
cosine_similarity: 1.0
|
119 |
})
|
120 |
|
121 |
+
send_update(MedicodeWeb.Components.TranscriptionTextComponent,
|
122 |
id: "transcription-chunk-#{socket.assigns.chunk.id}",
|
123 |
chunk: socket.assigns.chunk,
|
124 |
chunk_id: socket.assigns.chunk.id,
|
125 |
current_user: socket.assigns.current_user,
|
126 |
+
on_feedback: &MedicodeWeb.TranscriptionsLive.Show.on_feedback/1,
|
127 |
+
on_remove_code: &MedicodeWeb.TranscriptionsLive.Show.on_remove_code/1
|
128 |
)
|
129 |
|
130 |
Phoenix.PubSub.broadcast(
|
lib/medical_transcription_web/components/components.ex
CHANGED
@@ -1,14 +1,14 @@
|
|
1 |
-
defmodule
|
2 |
@moduledoc """
|
3 |
Functional UI components for the main transcription and coding view.
|
4 |
"""
|
5 |
|
6 |
use Phoenix.Component
|
7 |
-
use
|
8 |
|
9 |
-
import
|
10 |
|
11 |
-
alias
|
12 |
|
13 |
attr(:audio_upload, Phoenix.LiveView.UploadConfig, required: true)
|
14 |
|
@@ -70,7 +70,7 @@ defmodule MedicalTranscriptionWeb.Components do
|
|
70 |
"""
|
71 |
end
|
72 |
|
73 |
-
attr(:transcription,
|
74 |
attr(:summary_keywords, :list, default: [])
|
75 |
|
76 |
@doc """
|
@@ -95,7 +95,7 @@ defmodule MedicalTranscriptionWeb.Components do
|
|
95 |
|
96 |
<.record_button_in_heading status={@transcription.status} />
|
97 |
|
98 |
-
<div class="px-[14px] py-3 flex items-center gap-3 bg-brand rounded-lg text-white">
|
99 |
<img src={~p"/images/document.svg"} width="20" />
|
100 |
<span class="font-secondary"><%= @transcription.filename %></span>
|
101 |
</div>
|
|
|
1 |
+
defmodule MedicodeWeb.Components do
|
2 |
@moduledoc """
|
3 |
Functional UI components for the main transcription and coding view.
|
4 |
"""
|
5 |
|
6 |
use Phoenix.Component
|
7 |
+
use MedicodeWeb, :verified_routes
|
8 |
|
9 |
+
import MedicodeWeb.CoreComponents
|
10 |
|
11 |
+
alias MedicodeWeb.Components.TranscriptionTextComponent
|
12 |
|
13 |
attr(:audio_upload, Phoenix.LiveView.UploadConfig, required: true)
|
14 |
|
|
|
70 |
"""
|
71 |
end
|
72 |
|
73 |
+
attr(:transcription, Medicode.Transcriptions.Transcription, required: true)
|
74 |
attr(:summary_keywords, :list, default: [])
|
75 |
|
76 |
@doc """
|
|
|
95 |
|
96 |
<.record_button_in_heading status={@transcription.status} />
|
97 |
|
98 |
+
<div class="px-[14px] py-3 flex items-center gap-3 bg-brand rounded-lg text-white mr-4 overflow-hidden">
|
99 |
<img src={~p"/images/document.svg"} width="20" />
|
100 |
<span class="font-secondary"><%= @transcription.filename %></span>
|
101 |
</div>
|
lib/medical_transcription_web/components/core_components.ex
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
defmodule
|
2 |
@moduledoc """
|
3 |
Provides core UI components.
|
4 |
|
@@ -17,7 +17,7 @@ defmodule MedicalTranscriptionWeb.CoreComponents do
|
|
17 |
use Phoenix.Component
|
18 |
|
19 |
alias Phoenix.LiveView.JS
|
20 |
-
import
|
21 |
|
22 |
@doc """
|
23 |
Renders a modal.
|
@@ -659,9 +659,9 @@ defmodule MedicalTranscriptionWeb.CoreComponents do
|
|
659 |
# with our gettext backend as first argument. Translations are
|
660 |
# available in the errors.po file (as we use the "errors" domain).
|
661 |
if count = opts[:count] do
|
662 |
-
Gettext.dngettext(
|
663 |
else
|
664 |
-
Gettext.dgettext(
|
665 |
end
|
666 |
end
|
667 |
|
|
|
1 |
+
defmodule MedicodeWeb.CoreComponents do
|
2 |
@moduledoc """
|
3 |
Provides core UI components.
|
4 |
|
|
|
17 |
use Phoenix.Component
|
18 |
|
19 |
alias Phoenix.LiveView.JS
|
20 |
+
import MedicodeWeb.Gettext
|
21 |
|
22 |
@doc """
|
23 |
Renders a modal.
|
|
|
659 |
# with our gettext backend as first argument. Translations are
|
660 |
# available in the errors.po file (as we use the "errors" domain).
|
661 |
if count = opts[:count] do
|
662 |
+
Gettext.dngettext(MedicodeWeb.Gettext, "errors", msg, msg, count, opts)
|
663 |
else
|
664 |
+
Gettext.dgettext(MedicodeWeb.Gettext, "errors", msg, opts)
|
665 |
end
|
666 |
end
|
667 |
|
lib/medical_transcription_web/components/keyword_highlighter.ex
CHANGED
@@ -1,9 +1,9 @@
|
|
1 |
-
defmodule
|
2 |
@moduledoc """
|
3 |
Highlights keywords in transcription text by creating <span> tags for each.
|
4 |
"""
|
5 |
|
6 |
-
use
|
7 |
# alias Phoenix.HTML.Tag
|
8 |
|
9 |
attr :text, :string, required: true
|
|
|
1 |
+
defmodule MedicodeWeb.Components.KeywordHighlighter do
|
2 |
@moduledoc """
|
3 |
Highlights keywords in transcription text by creating <span> tags for each.
|
4 |
"""
|
5 |
|
6 |
+
use MedicodeWeb, :html
|
7 |
# alias Phoenix.HTML.Tag
|
8 |
|
9 |
attr :text, :string, required: true
|
lib/medical_transcription_web/components/layouts.ex
CHANGED
@@ -1,5 +1,5 @@
|
|
1 |
-
defmodule
|
2 |
-
use
|
3 |
|
4 |
embed_templates "layouts/*"
|
5 |
end
|
|
|
1 |
+
defmodule MedicodeWeb.Layouts do
|
2 |
+
use MedicodeWeb, :html
|
3 |
|
4 |
embed_templates "layouts/*"
|
5 |
end
|
lib/medical_transcription_web/components/layouts/root.html.heex
CHANGED
@@ -5,7 +5,7 @@
|
|
5 |
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
6 |
<meta name="csrf-token" content={get_csrf_token()} />
|
7 |
<.live_title suffix=" · Phoenix Framework">
|
8 |
-
<%= assigns[:page_title] || "
|
9 |
</.live_title>
|
10 |
<link phx-track-static rel="stylesheet" href={~p"/assets/app.css"} />
|
11 |
<script defer phx-track-static type="text/javascript" src={~p"/assets/app.js"}>
|
|
|
5 |
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
6 |
<meta name="csrf-token" content={get_csrf_token()} />
|
7 |
<.live_title suffix=" · Phoenix Framework">
|
8 |
+
<%= assigns[:page_title] || "Medicode" %>
|
9 |
</.live_title>
|
10 |
<link phx-track-static rel="stylesheet" href={~p"/assets/app.css"} />
|
11 |
<script defer phx-track-static type="text/javascript" src={~p"/assets/app.js"}>
|
lib/medical_transcription_web/components/sidebar_component.ex
CHANGED
@@ -1,12 +1,12 @@
|
|
1 |
-
defmodule
|
2 |
@moduledoc """
|
3 |
Main layout header component
|
4 |
"""
|
5 |
|
6 |
-
import
|
7 |
|
8 |
use Phoenix.Component
|
9 |
-
use
|
10 |
|
11 |
def sidebar(assigns) do
|
12 |
~H"""
|
@@ -68,7 +68,7 @@ defmodule MedicalTranscriptionWeb.Components.SidebarComponent do
|
|
68 |
|
69 |
<%= if @current_user do %>
|
70 |
<div class="px-6 flex flex-col items-center">
|
71 |
-
<%= if
|
72 |
<div
|
73 |
class="w-full px-3 py-2 bg-emerald-600 text-white text-center rounded-lg"
|
74 |
title="Precalculated vector embeddings for classification labels were found."
|
|
|
1 |
+
defmodule MedicodeWeb.Components.SidebarComponent do
|
2 |
@moduledoc """
|
3 |
Main layout header component
|
4 |
"""
|
5 |
|
6 |
+
import MedicodeWeb.CoreComponents
|
7 |
|
8 |
use Phoenix.Component
|
9 |
+
use MedicodeWeb, :verified_routes
|
10 |
|
11 |
def sidebar(assigns) do
|
12 |
~H"""
|
|
|
68 |
|
69 |
<%= if @current_user do %>
|
70 |
<div class="px-6 flex flex-col items-center">
|
71 |
+
<%= if Medicode.Coding.icd9_present?() do %>
|
72 |
<div
|
73 |
class="w-full px-3 py-2 bg-emerald-600 text-white text-center rounded-lg"
|
74 |
title="Precalculated vector embeddings for classification labels were found."
|
lib/medical_transcription_web/components/transcription_chunk_codings_component.ex
CHANGED
@@ -1,13 +1,13 @@
|
|
1 |
-
defmodule
|
2 |
@moduledoc """
|
3 |
Represents a portion of transcribed text.
|
4 |
"""
|
5 |
use Phoenix.Component
|
6 |
-
use
|
7 |
-
use
|
8 |
|
9 |
-
alias
|
10 |
-
alias
|
11 |
|
12 |
@impl Phoenix.LiveComponent
|
13 |
def render(assigns) do
|
@@ -157,13 +157,13 @@ defmodule MedicalTranscriptionWeb.Components.TranscriptionTextCodingsComponent d
|
|
157 |
end
|
158 |
|
159 |
defp data_feedback_for_code_vector(
|
160 |
-
%
|
161 |
"true" = _response
|
162 |
),
|
163 |
do: :positive
|
164 |
|
165 |
defp data_feedback_for_code_vector(
|
166 |
-
%
|
167 |
"false" = _response
|
168 |
),
|
169 |
do: :negative
|
|
|
1 |
+
defmodule MedicodeWeb.Components.TranscriptionTextCodingsComponent do
|
2 |
@moduledoc """
|
3 |
Represents a portion of transcribed text.
|
4 |
"""
|
5 |
use Phoenix.Component
|
6 |
+
use MedicodeWeb, :live_component
|
7 |
+
use MedicodeWeb, :verified_routes
|
8 |
|
9 |
+
alias Medicode.Transcriptions.TranscriptionChunkCodeVector
|
10 |
+
alias Medicode.Utilities
|
11 |
|
12 |
@impl Phoenix.LiveComponent
|
13 |
def render(assigns) do
|
|
|
157 |
end
|
158 |
|
159 |
defp data_feedback_for_code_vector(
|
160 |
+
%Medicode.Feedback.CodeFeedback{response: true},
|
161 |
"true" = _response
|
162 |
),
|
163 |
do: :positive
|
164 |
|
165 |
defp data_feedback_for_code_vector(
|
166 |
+
%Medicode.Feedback.CodeFeedback{response: false},
|
167 |
"false" = _response
|
168 |
),
|
169 |
do: :negative
|
lib/medical_transcription_web/components/transcription_text_component.ex
CHANGED
@@ -1,14 +1,14 @@
|
|
1 |
-
defmodule
|
2 |
@moduledoc """
|
3 |
Represents a portion of transcribed text.
|
4 |
"""
|
5 |
-
use
|
6 |
|
7 |
-
import
|
8 |
|
9 |
-
alias
|
10 |
-
alias
|
11 |
-
alias
|
12 |
|
13 |
@impl Phoenix.LiveComponent
|
14 |
def mount(socket) do
|
@@ -29,10 +29,10 @@ defmodule MedicalTranscriptionWeb.Components.TranscriptionTextComponent do
|
|
29 |
|
30 |
code_vectors_with_feedback =
|
31 |
assigns.chunk_id
|
32 |
-
|>
|
33 |
|> Enum.map(fn transcription_chunk_code_vector ->
|
34 |
code_feedback =
|
35 |
-
|
36 |
chunk.text,
|
37 |
assigns.current_user.id,
|
38 |
transcription_chunk_code_vector.code_vector_id
|
@@ -136,7 +136,7 @@ defmodule MedicalTranscriptionWeb.Components.TranscriptionTextComponent do
|
|
136 |
text: new_chunk_text_trimmed
|
137 |
})
|
138 |
|
139 |
-
|
140 |
|
141 |
{:noreply, assign(socket, :chunk, chunk)}
|
142 |
end
|
|
|
1 |
+
defmodule MedicodeWeb.Components.TranscriptionTextComponent do
|
2 |
@moduledoc """
|
3 |
Represents a portion of transcribed text.
|
4 |
"""
|
5 |
+
use MedicodeWeb, :live_component
|
6 |
|
7 |
+
import MedicodeWeb.Components.KeywordHighlighter
|
8 |
|
9 |
+
alias Medicode.Transcriptions
|
10 |
+
alias MedicodeWeb.Components.CodeSelect
|
11 |
+
alias MedicodeWeb.Components.TranscriptionTextCodingsComponent
|
12 |
|
13 |
@impl Phoenix.LiveComponent
|
14 |
def mount(socket) do
|
|
|
29 |
|
30 |
code_vectors_with_feedback =
|
31 |
assigns.chunk_id
|
32 |
+
|> Medicode.Transcriptions.list_transcription_chunk_code_vectors()
|
33 |
|> Enum.map(fn transcription_chunk_code_vector ->
|
34 |
code_feedback =
|
35 |
+
Medicode.Feedback.code_feedback_for_user_and_code_vector(
|
36 |
chunk.text,
|
37 |
assigns.current_user.id,
|
38 |
transcription_chunk_code_vector.code_vector_id
|
|
|
136 |
text: new_chunk_text_trimmed
|
137 |
})
|
138 |
|
139 |
+
Medicode.ClassificationSupervisor.start_classification(chunk)
|
140 |
|
141 |
{:noreply, assign(socket, :chunk, chunk)}
|
142 |
end
|
lib/medical_transcription_web/controllers/error_html.ex
CHANGED
@@ -1,5 +1,5 @@
|
|
1 |
-
defmodule
|
2 |
-
use
|
3 |
|
4 |
# If you want to customize your error pages,
|
5 |
# uncomment the embed_templates/1 call below
|
|
|
1 |
+
defmodule MedicodeWeb.ErrorHTML do
|
2 |
+
use MedicodeWeb, :html
|
3 |
|
4 |
# If you want to customize your error pages,
|
5 |
# uncomment the embed_templates/1 call below
|
lib/medical_transcription_web/controllers/error_html/404.html.heex
CHANGED
@@ -5,7 +5,7 @@
|
|
5 |
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
6 |
<meta name="csrf-token" content={get_csrf_token()} />
|
7 |
<.live_title suffix=" · Phoenix Framework">
|
8 |
-
<%= assigns[:page_title] || "
|
9 |
</.live_title>
|
10 |
<link phx-track-static rel="stylesheet" href={~p"/assets/app.css"} />
|
11 |
<script defer phx-track-static type="text/javascript" src={~p"/assets/app.js"}>
|
|
|
5 |
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
6 |
<meta name="csrf-token" content={get_csrf_token()} />
|
7 |
<.live_title suffix=" · Phoenix Framework">
|
8 |
+
<%= assigns[:page_title] || "Medicode" %>
|
9 |
</.live_title>
|
10 |
<link phx-track-static rel="stylesheet" href={~p"/assets/app.css"} />
|
11 |
<script defer phx-track-static type="text/javascript" src={~p"/assets/app.js"}>
|
lib/medical_transcription_web/controllers/error_json.ex
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
defmodule
|
2 |
# If you want to customize a particular status code,
|
3 |
# you may add your own clauses, such as:
|
4 |
#
|
|
|
1 |
+
defmodule MedicodeWeb.ErrorJSON do
|
2 |
# If you want to customize a particular status code,
|
3 |
# you may add your own clauses, such as:
|
4 |
#
|
lib/medical_transcription_web/controllers/page_controller.ex
CHANGED
@@ -1,5 +1,5 @@
|
|
1 |
-
defmodule
|
2 |
-
use
|
3 |
|
4 |
def home(conn, _params) do
|
5 |
# The home page is often custom made,
|
|
|
1 |
+
defmodule MedicodeWeb.PageController do
|
2 |
+
use MedicodeWeb, :controller
|
3 |
|
4 |
def home(conn, _params) do
|
5 |
# The home page is often custom made,
|
lib/medical_transcription_web/controllers/page_html.ex
CHANGED
@@ -1,5 +1,5 @@
|
|
1 |
-
defmodule
|
2 |
-
use
|
3 |
|
4 |
embed_templates "page_html/*"
|
5 |
end
|
|
|
1 |
+
defmodule MedicodeWeb.PageHTML do
|
2 |
+
use MedicodeWeb, :html
|
3 |
|
4 |
embed_templates "page_html/*"
|
5 |
end
|
lib/medical_transcription_web/controllers/user_session_controller.ex
CHANGED
@@ -1,8 +1,8 @@
|
|
1 |
-
defmodule
|
2 |
-
use
|
3 |
|
4 |
-
alias
|
5 |
-
alias
|
6 |
|
7 |
def create(conn, %{"_action" => "registered"} = params) do
|
8 |
create(conn, params, "Account created successfully!")
|
|
|
1 |
+
defmodule MedicodeWeb.UserSessionController do
|
2 |
+
use MedicodeWeb, :controller
|
3 |
|
4 |
+
alias Medicode.Accounts
|
5 |
+
alias MedicodeWeb.UserAuth
|
6 |
|
7 |
def create(conn, %{"_action" => "registered"} = params) do
|
8 |
create(conn, params, "Account created successfully!")
|