From 973400301ea741b6afdb8cacf882d8cd6aec70fc Mon Sep 17 00:00:00 2001 From: Lei Zhou Date: Fri, 6 Sep 2024 11:18:43 -0400 Subject: [PATCH 1/8] add Kindling, bulk fhir parser, sample bulk fhir data --- config/config.exs | 2 + lib/epiviewpoint/bulk_fhir_parser.ex | 161 +++++++++++++++ lib/epiviewpoint/cases.ex | 1 + lib/epiviewpoint/cases/import.ex | 9 + lib/epiviewpoint/data_file.ex | 8 + lib/epiviewpoint/r4/address.ex | 68 +++++++ lib/epiviewpoint/r4/age.ex | 47 +++++ lib/epiviewpoint/r4/annotation.ex | 46 +++++ lib/epiviewpoint/r4/attachment.ex | 44 +++++ lib/epiviewpoint/r4/codeable_concept.ex | 32 +++ lib/epiviewpoint/r4/coding.ex | 38 ++++ lib/epiviewpoint/r4/contact_detail.ex | 32 +++ lib/epiviewpoint/r4/contact_point.ex | 61 ++++++ lib/epiviewpoint/r4/contributor.ex | 43 ++++ lib/epiviewpoint/r4/count.ex | 47 +++++ lib/epiviewpoint/r4/data_requirement.ex | 57 ++++++ .../r4/data_requirement/code_filter.ex | 38 ++++ .../r4/data_requirement/date_filter.ex | 52 +++++ lib/epiviewpoint/r4/data_requirement/sort.ex | 41 ++++ lib/epiviewpoint/r4/distance.ex | 47 +++++ lib/epiviewpoint/r4/dosage.ex | 68 +++++++ lib/epiviewpoint/r4/dosage/dose_and_rate.ex | 60 ++++++ lib/epiviewpoint/r4/duration.ex | 47 +++++ lib/epiviewpoint/r4/element.ex | 26 +++ lib/epiviewpoint/r4/expression.ex | 46 +++++ lib/epiviewpoint/r4/extension.ex | 132 +++++++++++++ lib/epiviewpoint/r4/human_name.ex | 57 ++++++ lib/epiviewpoint/r4/identifier.ex | 52 +++++ lib/epiviewpoint/r4/meta.ex | 41 ++++ lib/epiviewpoint/r4/money.ex | 32 +++ lib/epiviewpoint/r4/narrative.ex | 43 ++++ lib/epiviewpoint/r4/observation.ex | 184 ++++++++++++++++++ lib/epiviewpoint/r4/observation/component.ex | 100 ++++++++++ .../r4/observation/reference_range.ex | 44 +++++ lib/epiviewpoint/r4/organization.ex | 68 +++++++ lib/epiviewpoint/r4/organization/contact.ex | 38 ++++ lib/epiviewpoint/r4/parameter_definition.ex | 42 ++++ lib/epiviewpoint/r4/patient.ex | 108 ++++++++++ lib/epiviewpoint/r4/patient/communication.ex | 36 ++++ lib/epiviewpoint/r4/patient/contact.ex | 53 +++++ lib/epiviewpoint/r4/patient/link.ex | 43 ++++ lib/epiviewpoint/r4/period.ex | 32 +++ lib/epiviewpoint/r4/quantity.ex | 47 +++++ lib/epiviewpoint/r4/range.ex | 32 +++ lib/epiviewpoint/r4/ratio.ex | 32 +++ lib/epiviewpoint/r4/reference.ex | 38 ++++ lib/epiviewpoint/r4/related_artifact.ex | 57 ++++++ lib/epiviewpoint/r4/resource_list.ex | 21 ++ lib/epiviewpoint/r4/sampled_data.ex | 44 +++++ lib/epiviewpoint/r4/signature.ex | 44 +++++ lib/epiviewpoint/r4/timing.ex | 37 ++++ lib/epiviewpoint/r4/timing/repeat.ex | 100 ++++++++++ lib/epiviewpoint/r4/trigger_definition.ex | 71 +++++++ lib/epiviewpoint/r4/usage_context.ex | 50 +++++ .../controllers/import_controller.ex | 26 +++ .../live/import_live.html.heex | 9 + lib/epiviewpoint_web/router.ex | 1 + lib/epiviewpoint_web/uploaded_file.ex | 4 + mix.exs | 1 + mix.lock | 10 + .../bulk_fhir_download/Observation.ndjson | 2 + .../bulk_fhir_download/Organization.ndjson | 2 + sample_data/bulk_fhir_download/Patient.ndjson | 2 + 63 files changed, 2856 insertions(+) create mode 100644 lib/epiviewpoint/bulk_fhir_parser.ex create mode 100644 lib/epiviewpoint/r4/address.ex create mode 100644 lib/epiviewpoint/r4/age.ex create mode 100644 lib/epiviewpoint/r4/annotation.ex create mode 100644 lib/epiviewpoint/r4/attachment.ex create mode 100644 lib/epiviewpoint/r4/codeable_concept.ex create mode 100644 lib/epiviewpoint/r4/coding.ex create mode 100644 lib/epiviewpoint/r4/contact_detail.ex create mode 100644 lib/epiviewpoint/r4/contact_point.ex create mode 100644 lib/epiviewpoint/r4/contributor.ex create mode 100644 lib/epiviewpoint/r4/count.ex create mode 100644 lib/epiviewpoint/r4/data_requirement.ex create mode 100644 lib/epiviewpoint/r4/data_requirement/code_filter.ex create mode 100644 lib/epiviewpoint/r4/data_requirement/date_filter.ex create mode 100644 lib/epiviewpoint/r4/data_requirement/sort.ex create mode 100644 lib/epiviewpoint/r4/distance.ex create mode 100644 lib/epiviewpoint/r4/dosage.ex create mode 100644 lib/epiviewpoint/r4/dosage/dose_and_rate.ex create mode 100644 lib/epiviewpoint/r4/duration.ex create mode 100644 lib/epiviewpoint/r4/element.ex create mode 100644 lib/epiviewpoint/r4/expression.ex create mode 100644 lib/epiviewpoint/r4/extension.ex create mode 100644 lib/epiviewpoint/r4/human_name.ex create mode 100644 lib/epiviewpoint/r4/identifier.ex create mode 100644 lib/epiviewpoint/r4/meta.ex create mode 100644 lib/epiviewpoint/r4/money.ex create mode 100644 lib/epiviewpoint/r4/narrative.ex create mode 100644 lib/epiviewpoint/r4/observation.ex create mode 100644 lib/epiviewpoint/r4/observation/component.ex create mode 100644 lib/epiviewpoint/r4/observation/reference_range.ex create mode 100644 lib/epiviewpoint/r4/organization.ex create mode 100644 lib/epiviewpoint/r4/organization/contact.ex create mode 100644 lib/epiviewpoint/r4/parameter_definition.ex create mode 100644 lib/epiviewpoint/r4/patient.ex create mode 100644 lib/epiviewpoint/r4/patient/communication.ex create mode 100644 lib/epiviewpoint/r4/patient/contact.ex create mode 100644 lib/epiviewpoint/r4/patient/link.ex create mode 100644 lib/epiviewpoint/r4/period.ex create mode 100644 lib/epiviewpoint/r4/quantity.ex create mode 100644 lib/epiviewpoint/r4/range.ex create mode 100644 lib/epiviewpoint/r4/ratio.ex create mode 100644 lib/epiviewpoint/r4/reference.ex create mode 100644 lib/epiviewpoint/r4/related_artifact.ex create mode 100644 lib/epiviewpoint/r4/resource_list.ex create mode 100644 lib/epiviewpoint/r4/sampled_data.ex create mode 100644 lib/epiviewpoint/r4/signature.ex create mode 100644 lib/epiviewpoint/r4/timing.ex create mode 100644 lib/epiviewpoint/r4/timing/repeat.ex create mode 100644 lib/epiviewpoint/r4/trigger_definition.ex create mode 100644 lib/epiviewpoint/r4/usage_context.ex create mode 100644 sample_data/bulk_fhir_download/Observation.ndjson create mode 100644 sample_data/bulk_fhir_download/Organization.ndjson create mode 100644 sample_data/bulk_fhir_download/Patient.ndjson diff --git a/config/config.exs b/config/config.exs index b4c9e1e9..84a71c71 100644 --- a/config/config.exs +++ b/config/config.exs @@ -50,6 +50,8 @@ config :dart_sass, cd: Path.expand("../assets", __DIR__) ] +config :kindling, root_resources: ["Patient", "Observation", "Organization"] + # Import environment specific config. This must remain at the bottom # of this file so it overrides the configuration defined above. import_config "#{Mix.env()}.exs" diff --git a/lib/epiviewpoint/bulk_fhir_parser.ex b/lib/epiviewpoint/bulk_fhir_parser.ex new file mode 100644 index 00000000..1f4dac32 --- /dev/null +++ b/lib/epiviewpoint/bulk_fhir_parser.ex @@ -0,0 +1,161 @@ +defmodule EpiViewpoint.BulkFhirParser do + def parse_bulk_fhir(file_list) do + file_list + |> load_resources() + |> extract_resources() + |> join_resources() + |> to_map() + end + + def load_resources(file_list) do + file_list + |> Enum.reduce(%{}, fn file, acc -> + resources = load_resource_file(file.contents) + + Enum.reduce(resources, acc, fn resource, inner_acc -> + type = resource.resource_type + Map.update(inner_acc, type, [resource], &[resource | &1]) + end) + end) + end + + def load_resource_file(file_content) do + file_content + |> String.split("\n") + |> Stream.map(&String.trim/1) + |> Stream.map(&json_to_kindle_schema(&1, "EpiViewpoint.R4")) + |> Enum.to_list() + end + + def extract_resources(resources) do + resources + |> Enum.reduce(%{}, fn {resource_type, resource_list}, acc -> + Map.put(acc, resource_type, extract_resource(resource_type, resource_list)) + end) + end + + def extract_resource("Patient", resource_list) do + resource_list + |> Enum.map(fn + %Epiviewpoint.R4.Patient{ + id: caseid, + identifier: [%Epiviewpoint.R4.Identifier{value: person_tid}], + name: [%Epiviewpoint.R4.HumanName{given: [search_firstname], family: search_lastname}], + birth_date: dateofbirth, + gender: sex, + address: [ + %Epiviewpoint.R4.Address{ + line: [diagaddress_street1], + city: diagaddress_city, + state: diagaddress_state, + postal_code: diagaddress_zip + } + ], + telecom: [%Epiviewpoint.R4.ContactPoint{value: phonenumber}], + extension: extensions + } = _resource -> + %{ + caseid: caseid, + person_tid: person_tid, + search_firstname: search_firstname, + search_lastname: search_lastname, + dateofbirth: Calendar.strftime(dateofbirth, "%m/%d/%Y"), + sex: sex |> to_string() |> String.capitalize(), + diagaddress_street1: diagaddress_street1, + diagaddress_city: diagaddress_city, + diagaddress_state: diagaddress_state, + diagaddress_zip: diagaddress_zip, + phonenumber: phonenumber, + ethnicity: find_extension(extensions, "http://hl7.org/fhir/us/core/StructureDefinition/us-core-ethnicity"), + occupation: find_extension(extensions), + race: find_extension(extensions, "http://hl7.org/fhir/us/core/StructureDefinition/us-core-race") + } + end) + end + + def extract_resource("Observation", resource_list) do + resource_list + |> Enum.map(fn + %Epiviewpoint.R4.Observation{ + id: lab_result_tid, + subject: %Epiviewpoint.R4.Reference{reference: "Patient/" <> pat_id}, + effective_date_time: datecollected, + issued: resultdate, + code: %Epiviewpoint.R4.CodeableConcept{text: testname}, + interpretation: [%Epiviewpoint.R4.CodeableConcept{coding: [%Epiviewpoint.R4.Coding{display: result}]}], + performer: [%Epiviewpoint.R4.Reference{reference: "Organization/" <> org_id}], + extension: extensions + } = _resource -> + %{ + lab_result_tid: lab_result_tid, + pat_id: pat_id, + datecollected: datecollected, + resultdate: resultdate |> DateTime.to_date() |> Calendar.strftime("%m/%d/%Y"), + testname: testname, + result: result, + org_id: org_id, + datereportedtolhd: find_extension(extensions) + } + end) + end + + def extract_resource("Organization", resource_list) do + resource_list + |> Enum.map(fn + %Epiviewpoint.R4.Organization{ + id: orgnization_id, + name: orderingfacilityname + } = _resource -> + %{ + orgnization_id: orgnization_id, + orderingfacilityname: orderingfacilityname + } + end) + end + + # Helper function to find extension values + defp find_extension(extensions, url \\ nil) do + Enum.find_value(extensions, fn + %Epiviewpoint.R4.Extension{ + url: ^url, + extension: [%Epiviewpoint.R4.Extension{url: "ombCategory", value_coding: %Epiviewpoint.R4.Coding{display: value}}, _] + } -> + value + + %Epiviewpoint.R4.Extension{url: "http://hl7.org/fhir/StructureDefinition/patient-occupation", value_string: value} -> + value + + %Epiviewpoint.R4.Extension{url: "http://hl7.org/fhir/StructureDefinition/datereportedtolhd", value_date: value} -> + value + + _ -> + nil + end) + end + + defp json_to_kindle_schema(json, version_namespace) do + map = Jason.decode!(json) + Kindling.Converter.convert(version_namespace, map) + end + + def join_resources(resources) do + patients = Map.get(resources, "Patient", []) + observations = Map.get(resources, "Observation", []) + organizations = Map.get(resources, "Organization", []) + + observations + |> Enum.map(fn observation -> + patient = Enum.find(patients, &(&1.caseid == observation.pat_id)) + organization = Enum.find(organizations, &(&1.orgnization_id == observation.org_id)) + + observation + |> Map.merge(patient || %{}) + |> Map.merge(organization || %{}) + |> Map.drop([:pat_id, :org_id, :orgnization_id]) + end) + end + + def to_map(resources) do + %{file_name: "load.bulk_fhir", contents: resources} + end +end diff --git a/lib/epiviewpoint/cases.ex b/lib/epiviewpoint/cases.ex index e39db651..7929aff9 100644 --- a/lib/epiviewpoint/cases.ex +++ b/lib/epiviewpoint/cases.ex @@ -34,6 +34,7 @@ defmodule EpiViewpoint.Cases do def count_lab_results(), do: LabResult |> Repo.aggregate(:count) def create_lab_result!({attrs, audit_meta}), do: %LabResult{} |> change_lab_result(attrs) |> AuditingRepo.insert!(audit_meta) def import_lab_results(lab_result_data_file_string, originator), do: Import.import_data_file(lab_result_data_file_string, originator) + def import_bulk_fhir_lab_results(lab_result_data_file_list, originator), do: Import.import_bulk_fhir_data_file(lab_result_data_file_list, originator) def list_lab_results(), do: LabResult.Query.all() |> Repo.all() def preload_initiating_lab_result(case_investigations_or_nil), do: case_investigations_or_nil |> Repo.preload(:initiating_lab_result) def preload_lab_results(person_or_people_or_nil), do: person_or_people_or_nil |> Repo.preload(lab_results: LabResult.Query.display_order()) diff --git a/lib/epiviewpoint/cases/import.ex b/lib/epiviewpoint/cases/import.ex index e9a20f81..0a89c42b 100644 --- a/lib/epiviewpoint/cases/import.ex +++ b/lib/epiviewpoint/cases/import.ex @@ -1,6 +1,7 @@ defmodule EpiViewpoint.Cases.Import do alias EpiViewpoint.Accounts alias EpiViewpoint.AuditLog + alias EpiViewpoint.BulkFhirParser alias EpiViewpoint.Cases alias EpiViewpoint.Cases.Import alias EpiViewpoint.Cases.LabResult @@ -59,6 +60,13 @@ defmodule EpiViewpoint.Cases.Import do ] end + def import_bulk_fhir_data_file(lab_result_data_file_list, originator) do + lab_result_data_file_list + |> BulkFhirParser.parse_bulk_fhir() + |> import_data_file(originator) + end + + def import_data_file(_file, %{admin: false}), do: {:error, "Originator must be an admin"} def import_data_file(file, %Accounts.User{} = originator) do @@ -103,6 +111,7 @@ defmodule EpiViewpoint.Cases.Import do case Path.extname(file.file_name) do ".csv" -> DataFile.read(file.contents, :csv, &rename_headers/1, @fields) ".ndjson" -> DataFile.read(file.contents, :ndjson, &rename_headers/1, @fields) + ".bulk_fhir" -> DataFile.read(file.contents, :bulk_fhir, &rename_headers/1, @fields) _ -> {:error, "Unsupported file type: #{file.extension}"} end end diff --git a/lib/epiviewpoint/data_file.ex b/lib/epiviewpoint/data_file.ex index cd645c9b..d34a2bf7 100644 --- a/lib/epiviewpoint/data_file.ex +++ b/lib/epiviewpoint/data_file.ex @@ -21,6 +21,10 @@ defmodule EpiViewpoint.DataFile do read(string, header_transformer, headers, &parse_ndjson/1) end + def read(string, :bulk_fhir, header_transformer, headers) when is_binary(string) do + read(string, header_transformer, headers, &parse_bulk_fhir/1) + end + def read(input, header_transformer, [required: required_headers, optional: optional_headers], parser) do with {:ok, df} <- parser.(input), {:ok, provided_headers} <- extract_provided_headers(df, header_transformer), @@ -84,6 +88,10 @@ defmodule EpiViewpoint.DataFile do DataFrame.load_ndjson(input) end + defp parse_bulk_fhir(input) do + DataFrame.new(input) + end + defp validate_csv(input) do EpiViewpoint.DataFile.Parser.parse_string(input, headers: true) input diff --git a/lib/epiviewpoint/r4/address.ex b/lib/epiviewpoint/r4/address.ex new file mode 100644 index 00000000..028b97cd --- /dev/null +++ b/lib/epiviewpoint/r4/address.ex @@ -0,0 +1,68 @@ +defmodule Epiviewpoint.R4.Address do + use Ecto.Schema + import Ecto.Changeset + + @fields [ + :city, + :country, + :district, + :id, + :line, + :postal_code, + :state, + :text, + :type, + :use + ] + @required_fields [] + + embedded_schema do + # Fields + field(:city, :string) + field(:country, :string) + field(:district, :string) + field(:postal_code, :string) + field(:state, :string) + field(:text, :string) + + field(:line, {:array, :string}) + + # Enum + field(:type, Ecto.Enum, + values: [ + :postal, + :physical, + :both + ] + ) + + field(:use, Ecto.Enum, + values: [ + :home, + :work, + :temp, + :old, + :billing + ] + ) + + # Embed One + embeds_one(:period, Epiviewpoint.R4.Period) + + # Embed Many + embeds_many(:extension, Epiviewpoint.R4.Extension) + end + + def choices(_), do: nil + + def version_namespace, do: Epiviewpoint.R4 + def version, do: "R4" + + def changeset(data \\ %__MODULE__{}, attrs) do + data + |> cast(attrs, @fields) + |> cast_embed(:period) + |> cast_embed(:extension) + |> validate_required(@required_fields) + end +end \ No newline at end of file diff --git a/lib/epiviewpoint/r4/age.ex b/lib/epiviewpoint/r4/age.ex new file mode 100644 index 00000000..df1305c5 --- /dev/null +++ b/lib/epiviewpoint/r4/age.ex @@ -0,0 +1,47 @@ +defmodule Epiviewpoint.R4.Age do + use Ecto.Schema + import Ecto.Changeset + + @fields [ + :code, + :comparator, + :id, + :system, + :unit, + :value + ] + @required_fields [] + + embedded_schema do + # Fields + field(:code, :string) + field(:system, :string) + field(:unit, :string) + field(:value, :decimal) + + # Enum + field(:comparator, Ecto.Enum, + values: [ + :<, + :<=, + :>=, + :> + ] + ) + + # Embed Many + embeds_many(:extension, Epiviewpoint.R4.Extension) + end + + def choices(_), do: nil + + def version_namespace, do: Epiviewpoint.R4 + def version, do: "R4" + + def changeset(data \\ %__MODULE__{}, attrs) do + data + |> cast(attrs, @fields) + |> cast_embed(:extension) + |> validate_required(@required_fields) + end +end \ No newline at end of file diff --git a/lib/epiviewpoint/r4/annotation.ex b/lib/epiviewpoint/r4/annotation.ex new file mode 100644 index 00000000..bd103fc7 --- /dev/null +++ b/lib/epiviewpoint/r4/annotation.ex @@ -0,0 +1,46 @@ +defmodule Epiviewpoint.R4.Annotation do + use Ecto.Schema + import Ecto.Changeset + + @fields [ + :author_string, + :id, + :text, + :time + ] + @required_fields [] + + embedded_schema do + # Fields + field(:author_string, :string) + field(:text, :string) + field(:time, :utc_datetime_usec) + + # Embed One + embeds_one(:author_reference, Epiviewpoint.R4.Reference) + + # Embed Many + embeds_many(:extension, Epiviewpoint.R4.Extension) + end + + def choices("author") do + [:author_reference, :author_string] + end + + def choices("authorReference"), do: :error + + def choices("authorstring"), do: :error + + def choices(_), do: nil + + def version_namespace, do: Epiviewpoint.R4 + def version, do: "R4" + + def changeset(data \\ %__MODULE__{}, attrs) do + data + |> cast(attrs, @fields) + |> cast_embed(:author_reference) + |> cast_embed(:extension) + |> validate_required(@required_fields) + end +end \ No newline at end of file diff --git a/lib/epiviewpoint/r4/attachment.ex b/lib/epiviewpoint/r4/attachment.ex new file mode 100644 index 00000000..6d3121b1 --- /dev/null +++ b/lib/epiviewpoint/r4/attachment.ex @@ -0,0 +1,44 @@ +defmodule Epiviewpoint.R4.Attachment do + use Ecto.Schema + import Ecto.Changeset + + @fields [ + :content_type, + :creation, + :data, + :hash, + :id, + :language, + :size, + :title, + :url + ] + @required_fields [] + + embedded_schema do + # Fields + field(:content_type, :string) + field(:creation, :utc_datetime_usec) + field(:data, :string) + field(:hash, :string) + field(:language, :string) + field(:size, :integer) + field(:title, :string) + field(:url, :string) + + # Embed Many + embeds_many(:extension, Epiviewpoint.R4.Extension) + end + + def choices(_), do: nil + + def version_namespace, do: Epiviewpoint.R4 + def version, do: "R4" + + def changeset(data \\ %__MODULE__{}, attrs) do + data + |> cast(attrs, @fields) + |> cast_embed(:extension) + |> validate_required(@required_fields) + end +end \ No newline at end of file diff --git a/lib/epiviewpoint/r4/codeable_concept.ex b/lib/epiviewpoint/r4/codeable_concept.ex new file mode 100644 index 00000000..a7924e17 --- /dev/null +++ b/lib/epiviewpoint/r4/codeable_concept.ex @@ -0,0 +1,32 @@ +defmodule Epiviewpoint.R4.CodeableConcept do + use Ecto.Schema + import Ecto.Changeset + + @fields [ + :id, + :text + ] + @required_fields [] + + embedded_schema do + # Fields + field(:text, :string) + + # Embed Many + embeds_many(:coding, Epiviewpoint.R4.Coding) + embeds_many(:extension, Epiviewpoint.R4.Extension) + end + + def choices(_), do: nil + + def version_namespace, do: Epiviewpoint.R4 + def version, do: "R4" + + def changeset(data \\ %__MODULE__{}, attrs) do + data + |> cast(attrs, @fields) + |> cast_embed(:coding) + |> cast_embed(:extension) + |> validate_required(@required_fields) + end +end \ No newline at end of file diff --git a/lib/epiviewpoint/r4/coding.ex b/lib/epiviewpoint/r4/coding.ex new file mode 100644 index 00000000..8f9edad0 --- /dev/null +++ b/lib/epiviewpoint/r4/coding.ex @@ -0,0 +1,38 @@ +defmodule Epiviewpoint.R4.Coding do + use Ecto.Schema + import Ecto.Changeset + + @fields [ + :code, + :display, + :id, + :system, + :user_selected, + :version + ] + @required_fields [] + + embedded_schema do + # Fields + field(:code, :string) + field(:display, :string) + field(:system, :string) + field(:user_selected, :boolean) + field(:version, :string) + + # Embed Many + embeds_many(:extension, Epiviewpoint.R4.Extension) + end + + def choices(_), do: nil + + def version_namespace, do: Epiviewpoint.R4 + def version, do: "R4" + + def changeset(data \\ %__MODULE__{}, attrs) do + data + |> cast(attrs, @fields) + |> cast_embed(:extension) + |> validate_required(@required_fields) + end +end \ No newline at end of file diff --git a/lib/epiviewpoint/r4/contact_detail.ex b/lib/epiviewpoint/r4/contact_detail.ex new file mode 100644 index 00000000..1439daca --- /dev/null +++ b/lib/epiviewpoint/r4/contact_detail.ex @@ -0,0 +1,32 @@ +defmodule Epiviewpoint.R4.ContactDetail do + use Ecto.Schema + import Ecto.Changeset + + @fields [ + :id, + :name + ] + @required_fields [] + + embedded_schema do + # Fields + field(:name, :string) + + # Embed Many + embeds_many(:extension, Epiviewpoint.R4.Extension) + embeds_many(:telecom, Epiviewpoint.R4.ContactPoint) + end + + def choices(_), do: nil + + def version_namespace, do: Epiviewpoint.R4 + def version, do: "R4" + + def changeset(data \\ %__MODULE__{}, attrs) do + data + |> cast(attrs, @fields) + |> cast_embed(:extension) + |> cast_embed(:telecom) + |> validate_required(@required_fields) + end +end \ No newline at end of file diff --git a/lib/epiviewpoint/r4/contact_point.ex b/lib/epiviewpoint/r4/contact_point.ex new file mode 100644 index 00000000..a23e86ab --- /dev/null +++ b/lib/epiviewpoint/r4/contact_point.ex @@ -0,0 +1,61 @@ +defmodule Epiviewpoint.R4.ContactPoint do + use Ecto.Schema + import Ecto.Changeset + + @fields [ + :id, + :rank, + :system, + :use, + :value + ] + @required_fields [] + + embedded_schema do + # Fields + field(:rank, :integer) + field(:value, :string) + + # Enum + field(:system, Ecto.Enum, + values: [ + :phone, + :fax, + :email, + :pager, + :url, + :sms, + :other + ] + ) + + field(:use, Ecto.Enum, + values: [ + :home, + :work, + :temp, + :old, + :mobile + ] + ) + + # Embed One + embeds_one(:period, Epiviewpoint.R4.Period) + + # Embed Many + embeds_many(:extension, Epiviewpoint.R4.Extension) + end + + def choices(_), do: nil + + def version_namespace, do: Epiviewpoint.R4 + def version, do: "R4" + + def changeset(data \\ %__MODULE__{}, attrs) do + data + |> cast(attrs, @fields) + |> cast_embed(:period) + |> cast_embed(:extension) + |> validate_required(@required_fields) + end +end \ No newline at end of file diff --git a/lib/epiviewpoint/r4/contributor.ex b/lib/epiviewpoint/r4/contributor.ex new file mode 100644 index 00000000..c7241ca0 --- /dev/null +++ b/lib/epiviewpoint/r4/contributor.ex @@ -0,0 +1,43 @@ +defmodule Epiviewpoint.R4.Contributor do + use Ecto.Schema + import Ecto.Changeset + + @fields [ + :id, + :name, + :type + ] + @required_fields [] + + embedded_schema do + # Fields + field(:name, :string) + + # Enum + field(:type, Ecto.Enum, + values: [ + :author, + :editor, + :reviewer, + :endorser + ] + ) + + # Embed Many + embeds_many(:contact, Epiviewpoint.R4.ContactDetail) + embeds_many(:extension, Epiviewpoint.R4.Extension) + end + + def choices(_), do: nil + + def version_namespace, do: Epiviewpoint.R4 + def version, do: "R4" + + def changeset(data \\ %__MODULE__{}, attrs) do + data + |> cast(attrs, @fields) + |> cast_embed(:contact) + |> cast_embed(:extension) + |> validate_required(@required_fields) + end +end \ No newline at end of file diff --git a/lib/epiviewpoint/r4/count.ex b/lib/epiviewpoint/r4/count.ex new file mode 100644 index 00000000..89bfe065 --- /dev/null +++ b/lib/epiviewpoint/r4/count.ex @@ -0,0 +1,47 @@ +defmodule Epiviewpoint.R4.Count do + use Ecto.Schema + import Ecto.Changeset + + @fields [ + :code, + :comparator, + :id, + :system, + :unit, + :value + ] + @required_fields [] + + embedded_schema do + # Fields + field(:code, :string) + field(:system, :string) + field(:unit, :string) + field(:value, :decimal) + + # Enum + field(:comparator, Ecto.Enum, + values: [ + :<, + :<=, + :>=, + :> + ] + ) + + # Embed Many + embeds_many(:extension, Epiviewpoint.R4.Extension) + end + + def choices(_), do: nil + + def version_namespace, do: Epiviewpoint.R4 + def version, do: "R4" + + def changeset(data \\ %__MODULE__{}, attrs) do + data + |> cast(attrs, @fields) + |> cast_embed(:extension) + |> validate_required(@required_fields) + end +end \ No newline at end of file diff --git a/lib/epiviewpoint/r4/data_requirement.ex b/lib/epiviewpoint/r4/data_requirement.ex new file mode 100644 index 00000000..546433d9 --- /dev/null +++ b/lib/epiviewpoint/r4/data_requirement.ex @@ -0,0 +1,57 @@ +defmodule Epiviewpoint.R4.DataRequirement do + use Ecto.Schema + import Ecto.Changeset + + @fields [ + :id, + :limit, + :must_support, + :profile, + :type + ] + @required_fields [] + + embedded_schema do + # Fields + field(:limit, :integer) + field(:type, :string) + + field(:must_support, {:array, :string}) + field(:profile, {:array, :string}) + + # Embed One + embeds_one(:subject_codeable_concept, Epiviewpoint.R4.CodeableConcept) + embeds_one(:subject_reference, Epiviewpoint.R4.Reference) + + # Embed Many + embeds_many(:code_filter, Epiviewpoint.R4.DataRequirement.CodeFilter) + embeds_many(:date_filter, Epiviewpoint.R4.DataRequirement.DateFilter) + embeds_many(:extension, Epiviewpoint.R4.Extension) + embeds_many(:sort, Epiviewpoint.R4.DataRequirement.Sort) + end + + def choices("subject") do + [:subject_codeable_concept, :subject_reference] + end + + def choices("subjectCodeableConcept"), do: :error + + def choices("subjectReference"), do: :error + + def choices(_), do: nil + + def version_namespace, do: Epiviewpoint.R4 + def version, do: "R4" + + def changeset(data \\ %__MODULE__{}, attrs) do + data + |> cast(attrs, @fields) + |> cast_embed(:subject_codeable_concept) + |> cast_embed(:subject_reference) + |> cast_embed(:code_filter) + |> cast_embed(:date_filter) + |> cast_embed(:extension) + |> cast_embed(:sort) + |> validate_required(@required_fields) + end +end \ No newline at end of file diff --git a/lib/epiviewpoint/r4/data_requirement/code_filter.ex b/lib/epiviewpoint/r4/data_requirement/code_filter.ex new file mode 100644 index 00000000..6c5d50ff --- /dev/null +++ b/lib/epiviewpoint/r4/data_requirement/code_filter.ex @@ -0,0 +1,38 @@ +defmodule Epiviewpoint.R4.DataRequirement.CodeFilter do + use Ecto.Schema + import Ecto.Changeset + + @fields [ + :id, + :path, + :search_param, + :value_set + ] + @required_fields [] + + embedded_schema do + # Fields + field(:path, :string) + field(:search_param, :string) + field(:value_set, :string) + + # Embed Many + embeds_many(:code, Epiviewpoint.R4.Coding) + embeds_many(:extension, Epiviewpoint.R4.Extension) + embeds_many(:modifier_extension, Epiviewpoint.R4.Extension) + end + + def choices(_), do: nil + + def version_namespace, do: Epiviewpoint.R4 + def version, do: "R4" + + def changeset(data \\ %__MODULE__{}, attrs) do + data + |> cast(attrs, @fields) + |> cast_embed(:code) + |> cast_embed(:extension) + |> cast_embed(:modifier_extension) + |> validate_required(@required_fields) + end +end \ No newline at end of file diff --git a/lib/epiviewpoint/r4/data_requirement/date_filter.ex b/lib/epiviewpoint/r4/data_requirement/date_filter.ex new file mode 100644 index 00000000..2b9dce0f --- /dev/null +++ b/lib/epiviewpoint/r4/data_requirement/date_filter.ex @@ -0,0 +1,52 @@ +defmodule Epiviewpoint.R4.DataRequirement.DateFilter do + use Ecto.Schema + import Ecto.Changeset + + @fields [ + :id, + :path, + :search_param, + :value_date_time + ] + @required_fields [] + + embedded_schema do + # Fields + field(:path, :string) + field(:search_param, :string) + field(:value_date_time, :string) + + # Embed One + embeds_one(:value_duration, Epiviewpoint.R4.Duration) + embeds_one(:value_period, Epiviewpoint.R4.Period) + + # Embed Many + embeds_many(:extension, Epiviewpoint.R4.Extension) + embeds_many(:modifier_extension, Epiviewpoint.R4.Extension) + end + + def choices("value") do + [:value_date_time, :value_period, :value_duration] + end + + def choices("valuedateTime"), do: :error + + def choices("valuePeriod"), do: :error + + def choices("valueDuration"), do: :error + + def choices(_), do: nil + + def version_namespace, do: Epiviewpoint.R4 + def version, do: "R4" + + def changeset(data \\ %__MODULE__{}, attrs) do + data + |> cast(attrs, @fields) + |> cast_embed(:value_duration) + |> cast_embed(:value_period) + |> cast_embed(:extension) + |> cast_embed(:modifier_extension) + |> validate_required(@required_fields) + end +end \ No newline at end of file diff --git a/lib/epiviewpoint/r4/data_requirement/sort.ex b/lib/epiviewpoint/r4/data_requirement/sort.ex new file mode 100644 index 00000000..f465cf05 --- /dev/null +++ b/lib/epiviewpoint/r4/data_requirement/sort.ex @@ -0,0 +1,41 @@ +defmodule Epiviewpoint.R4.DataRequirement.Sort do + use Ecto.Schema + import Ecto.Changeset + + @fields [ + :direction, + :id, + :path + ] + @required_fields [] + + embedded_schema do + # Fields + field(:path, :string) + + # Enum + field(:direction, Ecto.Enum, + values: [ + :ascending, + :descending + ] + ) + + # Embed Many + embeds_many(:extension, Epiviewpoint.R4.Extension) + embeds_many(:modifier_extension, Epiviewpoint.R4.Extension) + end + + def choices(_), do: nil + + def version_namespace, do: Epiviewpoint.R4 + def version, do: "R4" + + def changeset(data \\ %__MODULE__{}, attrs) do + data + |> cast(attrs, @fields) + |> cast_embed(:extension) + |> cast_embed(:modifier_extension) + |> validate_required(@required_fields) + end +end \ No newline at end of file diff --git a/lib/epiviewpoint/r4/distance.ex b/lib/epiviewpoint/r4/distance.ex new file mode 100644 index 00000000..e70e5554 --- /dev/null +++ b/lib/epiviewpoint/r4/distance.ex @@ -0,0 +1,47 @@ +defmodule Epiviewpoint.R4.Distance do + use Ecto.Schema + import Ecto.Changeset + + @fields [ + :code, + :comparator, + :id, + :system, + :unit, + :value + ] + @required_fields [] + + embedded_schema do + # Fields + field(:code, :string) + field(:system, :string) + field(:unit, :string) + field(:value, :decimal) + + # Enum + field(:comparator, Ecto.Enum, + values: [ + :<, + :<=, + :>=, + :> + ] + ) + + # Embed Many + embeds_many(:extension, Epiviewpoint.R4.Extension) + end + + def choices(_), do: nil + + def version_namespace, do: Epiviewpoint.R4 + def version, do: "R4" + + def changeset(data \\ %__MODULE__{}, attrs) do + data + |> cast(attrs, @fields) + |> cast_embed(:extension) + |> validate_required(@required_fields) + end +end \ No newline at end of file diff --git a/lib/epiviewpoint/r4/dosage.ex b/lib/epiviewpoint/r4/dosage.ex new file mode 100644 index 00000000..0b2e21e4 --- /dev/null +++ b/lib/epiviewpoint/r4/dosage.ex @@ -0,0 +1,68 @@ +defmodule Epiviewpoint.R4.Dosage do + use Ecto.Schema + import Ecto.Changeset + + @fields [ + :as_needed_boolean, + :id, + :patient_instruction, + :sequence, + :text + ] + @required_fields [] + + embedded_schema do + # Fields + field(:as_needed_boolean, :boolean) + field(:patient_instruction, :string) + field(:sequence, :integer) + field(:text, :string) + + # Embed One + embeds_one(:as_needed_codeable_concept, Epiviewpoint.R4.CodeableConcept) + embeds_one(:max_dose_per_administration, Epiviewpoint.R4.Quantity) + embeds_one(:max_dose_per_lifetime, Epiviewpoint.R4.Quantity) + embeds_one(:max_dose_per_period, Epiviewpoint.R4.Ratio) + embeds_one(:method, Epiviewpoint.R4.CodeableConcept) + embeds_one(:route, Epiviewpoint.R4.CodeableConcept) + embeds_one(:site, Epiviewpoint.R4.CodeableConcept) + embeds_one(:timing, Epiviewpoint.R4.Timing) + + # Embed Many + embeds_many(:additional_instruction, Epiviewpoint.R4.CodeableConcept) + embeds_many(:dose_and_rate, Epiviewpoint.R4.Dosage.DoseAndRate) + embeds_many(:extension, Epiviewpoint.R4.Extension) + embeds_many(:modifier_extension, Epiviewpoint.R4.Extension) + end + + def choices("asNeeded") do + [:asNeeded_boolean, :asNeeded_codeable_concept] + end + + def choices("asNeededboolean"), do: :error + + def choices("asNeededCodeableConcept"), do: :error + + def choices(_), do: nil + + def version_namespace, do: Epiviewpoint.R4 + def version, do: "R4" + + def changeset(data \\ %__MODULE__{}, attrs) do + data + |> cast(attrs, @fields) + |> cast_embed(:as_needed_codeable_concept) + |> cast_embed(:max_dose_per_administration) + |> cast_embed(:max_dose_per_lifetime) + |> cast_embed(:max_dose_per_period) + |> cast_embed(:method) + |> cast_embed(:route) + |> cast_embed(:site) + |> cast_embed(:timing) + |> cast_embed(:additional_instruction) + |> cast_embed(:dose_and_rate) + |> cast_embed(:extension) + |> cast_embed(:modifier_extension) + |> validate_required(@required_fields) + end +end \ No newline at end of file diff --git a/lib/epiviewpoint/r4/dosage/dose_and_rate.ex b/lib/epiviewpoint/r4/dosage/dose_and_rate.ex new file mode 100644 index 00000000..2fff6029 --- /dev/null +++ b/lib/epiviewpoint/r4/dosage/dose_and_rate.ex @@ -0,0 +1,60 @@ +defmodule Epiviewpoint.R4.Dosage.DoseAndRate do + use Ecto.Schema + import Ecto.Changeset + + @fields [ + :id + ] + @required_fields [] + + embedded_schema do + # Embed One + embeds_one(:dose_quantity, Epiviewpoint.R4.Quantity) + embeds_one(:dose_range, Epiviewpoint.R4.Range) + embeds_one(:rate_quantity, Epiviewpoint.R4.Quantity) + embeds_one(:rate_range, Epiviewpoint.R4.Range) + embeds_one(:rate_ratio, Epiviewpoint.R4.Ratio) + embeds_one(:type, Epiviewpoint.R4.CodeableConcept) + + # Embed Many + embeds_many(:extension, Epiviewpoint.R4.Extension) + embeds_many(:modifier_extension, Epiviewpoint.R4.Extension) + end + + def choices("dose") do + [:dose_range, :dose_simple_quantity] + end + + def choices("doseRange"), do: :error + + def choices("doseSimpleQuantity"), do: :error + + def choices("rate") do + [:rate_ratio, :rate_range, :rate_simple_quantity] + end + + def choices("rateRatio"), do: :error + + def choices("rateRange"), do: :error + + def choices("rateSimpleQuantity"), do: :error + + def choices(_), do: nil + + def version_namespace, do: Epiviewpoint.R4 + def version, do: "R4" + + def changeset(data \\ %__MODULE__{}, attrs) do + data + |> cast(attrs, @fields) + |> cast_embed(:dose_quantity) + |> cast_embed(:dose_range) + |> cast_embed(:rate_quantity) + |> cast_embed(:rate_range) + |> cast_embed(:rate_ratio) + |> cast_embed(:type) + |> cast_embed(:extension) + |> cast_embed(:modifier_extension) + |> validate_required(@required_fields) + end +end \ No newline at end of file diff --git a/lib/epiviewpoint/r4/duration.ex b/lib/epiviewpoint/r4/duration.ex new file mode 100644 index 00000000..4c4cdb30 --- /dev/null +++ b/lib/epiviewpoint/r4/duration.ex @@ -0,0 +1,47 @@ +defmodule Epiviewpoint.R4.Duration do + use Ecto.Schema + import Ecto.Changeset + + @fields [ + :code, + :comparator, + :id, + :system, + :unit, + :value + ] + @required_fields [] + + embedded_schema do + # Fields + field(:code, :string) + field(:system, :string) + field(:unit, :string) + field(:value, :decimal) + + # Enum + field(:comparator, Ecto.Enum, + values: [ + :<, + :<=, + :>=, + :> + ] + ) + + # Embed Many + embeds_many(:extension, Epiviewpoint.R4.Extension) + end + + def choices(_), do: nil + + def version_namespace, do: Epiviewpoint.R4 + def version, do: "R4" + + def changeset(data \\ %__MODULE__{}, attrs) do + data + |> cast(attrs, @fields) + |> cast_embed(:extension) + |> validate_required(@required_fields) + end +end \ No newline at end of file diff --git a/lib/epiviewpoint/r4/element.ex b/lib/epiviewpoint/r4/element.ex new file mode 100644 index 00000000..5af02ad7 --- /dev/null +++ b/lib/epiviewpoint/r4/element.ex @@ -0,0 +1,26 @@ +defmodule Epiviewpoint.R4.Element do + use Ecto.Schema + import Ecto.Changeset + + @fields [ + :id + ] + @required_fields [] + + embedded_schema do + # Embed Many + embeds_many(:extension, Epiviewpoint.R4.Extension) + end + + def choices(_), do: nil + + def version_namespace, do: Epiviewpoint.R4 + def version, do: "R4" + + def changeset(data \\ %__MODULE__{}, attrs) do + data + |> cast(attrs, @fields) + |> cast_embed(:extension) + |> validate_required(@required_fields) + end +end \ No newline at end of file diff --git a/lib/epiviewpoint/r4/expression.ex b/lib/epiviewpoint/r4/expression.ex new file mode 100644 index 00000000..241c3e34 --- /dev/null +++ b/lib/epiviewpoint/r4/expression.ex @@ -0,0 +1,46 @@ +defmodule Epiviewpoint.R4.Expression do + use Ecto.Schema + import Ecto.Changeset + + @fields [ + :description, + :expression, + :id, + :language, + :name, + :reference + ] + @required_fields [] + + embedded_schema do + # Fields + field(:description, :string) + field(:expression, :string) + field(:name, :binary_id) + field(:reference, :string) + + # Enum + field(:language, Ecto.Enum, + values: [ + :"text/cql", + :"text/fhirpath", + :"application/x_fhir_query" + ] + ) + + # Embed Many + embeds_many(:extension, Epiviewpoint.R4.Extension) + end + + def choices(_), do: nil + + def version_namespace, do: Epiviewpoint.R4 + def version, do: "R4" + + def changeset(data \\ %__MODULE__{}, attrs) do + data + |> cast(attrs, @fields) + |> cast_embed(:extension) + |> validate_required(@required_fields) + end +end \ No newline at end of file diff --git a/lib/epiviewpoint/r4/extension.ex b/lib/epiviewpoint/r4/extension.ex new file mode 100644 index 00000000..c8ab3b50 --- /dev/null +++ b/lib/epiviewpoint/r4/extension.ex @@ -0,0 +1,132 @@ +defmodule Epiviewpoint.R4.Extension do + use Ecto.Schema + import Ecto.Changeset + + @fields [ + :value_time, + :value_positive_int, + :value_date_time, + :value_markdown, + :value_date, + :value_unsigned_int, + :value_string, + :value_url, + :value_uri, + :value_integer, + :value_canonical, + :value_instant, + :url, + :value_oid, + :value_code, + :value_base64_binary, + :value_decimal, + :id, + :value_uuid, + :value_boolean, + :value_id + ] + @required_fields [] + + embedded_schema do + # Fields + field(:value_time, :string) + field(:value_positive_int, :integer) + field(:value_date_time, :string) + field(:value_markdown, :string) + field(:value_date, :string) + field(:value_unsigned_int, :integer) + field(:value_string, :string) + field(:value_url, :string) + field(:value_uri, :string) + field(:value_integer, :integer) + field(:value_canonical, :string) + field(:value_instant, :string) + field(:url, :string) + field(:value_oid, :string) + field(:value_code, :string) + field(:value_base64_binary, :string) + field(:value_decimal, :decimal) + field(:value_uuid, :string) + field(:value_boolean, :boolean) + field(:value_id, :string) + + # Embed One + embeds_one(:value_quantity, Epiviewpoint.R4.Quantity) + embeds_one(:value_expression, Epiviewpoint.R4.Expression) + embeds_one(:value_attachment, Epiviewpoint.R4.Attachment) + embeds_one(:value_identifier, Epiviewpoint.R4.Identifier) + embeds_one(:value_sampled_data, Epiviewpoint.R4.SampledData) + embeds_one(:value_parameter_definition, Epiviewpoint.R4.ParameterDefinition) + embeds_one(:value_timing, Epiviewpoint.R4.Timing) + embeds_one(:value_reference, Epiviewpoint.R4.Reference) + embeds_one(:value_contact_point, Epiviewpoint.R4.ContactPoint) + embeds_one(:value_age, Epiviewpoint.R4.Age) + embeds_one(:value_meta, Epiviewpoint.R4.Meta) + embeds_one(:value_annotation, Epiviewpoint.R4.Annotation) + embeds_one(:value_money, Epiviewpoint.R4.Money) + embeds_one(:value_usage_context, Epiviewpoint.R4.UsageContext) + embeds_one(:value_related_artifact, Epiviewpoint.R4.RelatedArtifact) + embeds_one(:value_contact_detail, Epiviewpoint.R4.ContactDetail) + embeds_one(:value_ratio, Epiviewpoint.R4.Ratio) + embeds_one(:value_distance, Epiviewpoint.R4.Distance) + embeds_one(:value_duration, Epiviewpoint.R4.Duration) + embeds_one(:value_human_name, Epiviewpoint.R4.HumanName) + embeds_one(:value_period, Epiviewpoint.R4.Period) + embeds_one(:value_range, Epiviewpoint.R4.Range) + embeds_one(:value_dosage, Epiviewpoint.R4.Dosage) + embeds_one(:value_contributor, Epiviewpoint.R4.Contributor) + embeds_one(:value_address, Epiviewpoint.R4.Address) + embeds_one(:value_signature, Epiviewpoint.R4.Signature) + embeds_one(:value_trigger_definition, Epiviewpoint.R4.TriggerDefinition) + embeds_one(:value_data_requirement, Epiviewpoint.R4.DataRequirement) + embeds_one(:value_count, Epiviewpoint.R4.Count) + embeds_one(:value_coding, Epiviewpoint.R4.Coding) + embeds_one(:value_codeable_concept, Epiviewpoint.R4.CodeableConcept) + + # Embed Many + embeds_many(:extension, Epiviewpoint.R4.Extension) + end + + def choices(_), do: nil + + def version_namespace, do: Epiviewpoint.R4 + def version, do: "R4" + + def changeset(data \\ %__MODULE__{}, attrs) do + data + |> cast(attrs, @fields) + |> cast_embed(:value_quantity) + |> cast_embed(:value_expression) + |> cast_embed(:value_attachment) + |> cast_embed(:value_identifier) + |> cast_embed(:value_sampled_data) + |> cast_embed(:value_parameter_definition) + |> cast_embed(:value_timing) + |> cast_embed(:value_reference) + |> cast_embed(:value_contact_point) + |> cast_embed(:value_age) + |> cast_embed(:value_meta) + |> cast_embed(:value_annotation) + |> cast_embed(:value_money) + |> cast_embed(:value_usage_context) + |> cast_embed(:value_related_artifact) + |> cast_embed(:value_contact_detail) + |> cast_embed(:value_ratio) + |> cast_embed(:value_distance) + |> cast_embed(:value_duration) + |> cast_embed(:value_human_name) + |> cast_embed(:value_period) + |> cast_embed(:value_range) + |> cast_embed(:value_dosage) + |> cast_embed(:value_contributor) + |> cast_embed(:value_address) + |> cast_embed(:value_signature) + |> cast_embed(:value_trigger_definition) + |> cast_embed(:value_data_requirement) + |> cast_embed(:value_count) + |> cast_embed(:value_coding) + |> cast_embed(:value_codeable_concept) + |> cast_embed(:extension) + |> validate_required(@required_fields) + end +end \ No newline at end of file diff --git a/lib/epiviewpoint/r4/human_name.ex b/lib/epiviewpoint/r4/human_name.ex new file mode 100644 index 00000000..8ed99171 --- /dev/null +++ b/lib/epiviewpoint/r4/human_name.ex @@ -0,0 +1,57 @@ +defmodule Epiviewpoint.R4.HumanName do + use Ecto.Schema + import Ecto.Changeset + + @fields [ + :family, + :given, + :id, + :prefix, + :suffix, + :text, + :use + ] + @required_fields [] + + embedded_schema do + # Fields + field(:family, :string) + field(:text, :string) + + field(:given, {:array, :string}) + field(:prefix, {:array, :string}) + field(:suffix, {:array, :string}) + + # Enum + field(:use, Ecto.Enum, + values: [ + :usual, + :official, + :temp, + :nickname, + :anonymous, + :old, + :maiden + ] + ) + + # Embed One + embeds_one(:period, Epiviewpoint.R4.Period) + + # Embed Many + embeds_many(:extension, Epiviewpoint.R4.Extension) + end + + def choices(_), do: nil + + def version_namespace, do: Epiviewpoint.R4 + def version, do: "R4" + + def changeset(data \\ %__MODULE__{}, attrs) do + data + |> cast(attrs, @fields) + |> cast_embed(:period) + |> cast_embed(:extension) + |> validate_required(@required_fields) + end +end \ No newline at end of file diff --git a/lib/epiviewpoint/r4/identifier.ex b/lib/epiviewpoint/r4/identifier.ex new file mode 100644 index 00000000..d2471bc7 --- /dev/null +++ b/lib/epiviewpoint/r4/identifier.ex @@ -0,0 +1,52 @@ +defmodule Epiviewpoint.R4.Identifier do + use Ecto.Schema + import Ecto.Changeset + + @fields [ + :id, + :system, + :use, + :value + ] + @required_fields [] + + embedded_schema do + # Fields + field(:system, :string) + field(:value, :string) + + # Enum + field(:use, Ecto.Enum, + values: [ + :usual, + :official, + :temp, + :secondary, + :old + ] + ) + + # Embed One + embeds_one(:assigner, Epiviewpoint.R4.Reference) + embeds_one(:period, Epiviewpoint.R4.Period) + embeds_one(:type, Epiviewpoint.R4.CodeableConcept) + + # Embed Many + embeds_many(:extension, Epiviewpoint.R4.Extension) + end + + def choices(_), do: nil + + def version_namespace, do: Epiviewpoint.R4 + def version, do: "R4" + + def changeset(data \\ %__MODULE__{}, attrs) do + data + |> cast(attrs, @fields) + |> cast_embed(:assigner) + |> cast_embed(:period) + |> cast_embed(:type) + |> cast_embed(:extension) + |> validate_required(@required_fields) + end +end \ No newline at end of file diff --git a/lib/epiviewpoint/r4/meta.ex b/lib/epiviewpoint/r4/meta.ex new file mode 100644 index 00000000..8632b8b7 --- /dev/null +++ b/lib/epiviewpoint/r4/meta.ex @@ -0,0 +1,41 @@ +defmodule Epiviewpoint.R4.Meta do + use Ecto.Schema + import Ecto.Changeset + + @fields [ + :id, + :last_updated, + :profile, + :source, + :version_id + ] + @required_fields [] + + embedded_schema do + # Fields + field(:last_updated, :utc_datetime_usec) + field(:source, :string) + field(:version_id, :binary_id) + + field(:profile, {:array, :string}) + + # Embed Many + embeds_many(:extension, Epiviewpoint.R4.Extension) + embeds_many(:security, Epiviewpoint.R4.Coding) + embeds_many(:tag, Epiviewpoint.R4.Coding) + end + + def choices(_), do: nil + + def version_namespace, do: Epiviewpoint.R4 + def version, do: "R4" + + def changeset(data \\ %__MODULE__{}, attrs) do + data + |> cast(attrs, @fields) + |> cast_embed(:extension) + |> cast_embed(:security) + |> cast_embed(:tag) + |> validate_required(@required_fields) + end +end \ No newline at end of file diff --git a/lib/epiviewpoint/r4/money.ex b/lib/epiviewpoint/r4/money.ex new file mode 100644 index 00000000..6014c0af --- /dev/null +++ b/lib/epiviewpoint/r4/money.ex @@ -0,0 +1,32 @@ +defmodule Epiviewpoint.R4.Money do + use Ecto.Schema + import Ecto.Changeset + + @fields [ + :currency, + :id, + :value + ] + @required_fields [] + + embedded_schema do + # Fields + field(:currency, :string) + field(:value, :decimal) + + # Embed Many + embeds_many(:extension, Epiviewpoint.R4.Extension) + end + + def choices(_), do: nil + + def version_namespace, do: Epiviewpoint.R4 + def version, do: "R4" + + def changeset(data \\ %__MODULE__{}, attrs) do + data + |> cast(attrs, @fields) + |> cast_embed(:extension) + |> validate_required(@required_fields) + end +end \ No newline at end of file diff --git a/lib/epiviewpoint/r4/narrative.ex b/lib/epiviewpoint/r4/narrative.ex new file mode 100644 index 00000000..6798aa4d --- /dev/null +++ b/lib/epiviewpoint/r4/narrative.ex @@ -0,0 +1,43 @@ +defmodule Epiviewpoint.R4.Narrative do + use Ecto.Schema + import Ecto.Changeset + + @fields [ + :div, + :id, + :status + ] + @required_fields [ + :div + ] + + embedded_schema do + # Fields + field(:div, :string) + + # Enum + field(:status, Ecto.Enum, + values: [ + :generated, + :extensions, + :additional, + :empty + ] + ) + + # Embed Many + embeds_many(:extension, Epiviewpoint.R4.Extension) + end + + def choices(_), do: nil + + def version_namespace, do: Epiviewpoint.R4 + def version, do: "R4" + + def changeset(data \\ %__MODULE__{}, attrs) do + data + |> cast(attrs, @fields) + |> cast_embed(:extension) + |> validate_required(@required_fields) + end +end \ No newline at end of file diff --git a/lib/epiviewpoint/r4/observation.ex b/lib/epiviewpoint/r4/observation.ex new file mode 100644 index 00000000..81449b4c --- /dev/null +++ b/lib/epiviewpoint/r4/observation.ex @@ -0,0 +1,184 @@ +defmodule Epiviewpoint.R4.Observation do + use Ecto.Schema + import Ecto.Changeset + + @fields [ + :value_time, + :effective_date_time, + :value_date_time, + :effective_instant, + :value_string, + :language, + :value_integer, + :implicit_rules, + :status, + :id, + :issued, + :value_boolean + ] + @required_fields [] + + @primary_key {:id, :binary_id, autogenerate: true} + schema "observation" do + # Constants + field(:resource_type, :string, virtual: true, default: "Observation") + + # Fields + field(:value_time, :string) + field(:effective_date_time, :string) + field(:value_date_time, :string) + field(:effective_instant, :string) + field(:value_string, :string) + field(:language, :string) + field(:value_integer, :integer) + field(:implicit_rules, :string) + field(:issued, :utc_datetime_usec) + field(:value_boolean, :boolean) + + # Enum + field(:status, Ecto.Enum, + values: [ + :registered, + :preliminary, + :final, + :amended, + :corrected, + :cancelled, + :entered_in_error, + :unknown + ] + ) + + # Embed One + embeds_one(:value_quantity, Epiviewpoint.R4.Quantity) + embeds_one(:effective_timing, Epiviewpoint.R4.Timing) + embeds_one(:value_sampled_data, Epiviewpoint.R4.SampledData) + embeds_one(:specimen, Epiviewpoint.R4.Reference) + embeds_one(:effective_period, Epiviewpoint.R4.Period) + embeds_one(:value_ratio, Epiviewpoint.R4.Ratio) + embeds_one(:encounter, Epiviewpoint.R4.Reference) + embeds_one(:code, Epiviewpoint.R4.CodeableConcept) + embeds_one(:subject, Epiviewpoint.R4.Reference) + embeds_one(:data_absent_reason, Epiviewpoint.R4.CodeableConcept) + embeds_one(:text, Epiviewpoint.R4.Narrative) + embeds_one(:body_site, Epiviewpoint.R4.CodeableConcept) + embeds_one(:meta, Epiviewpoint.R4.Meta) + embeds_one(:value_period, Epiviewpoint.R4.Period) + embeds_one(:value_range, Epiviewpoint.R4.Range) + embeds_one(:method, Epiviewpoint.R4.CodeableConcept) + embeds_one(:device, Epiviewpoint.R4.Reference) + embeds_one(:value_codeable_concept, Epiviewpoint.R4.CodeableConcept) + + # Embed Many + embeds_many(:extension, Epiviewpoint.R4.Extension) + embeds_many(:contained, Epiviewpoint.R4.ResourceList) + embeds_many(:reference_range, Epiviewpoint.R4.Observation.ReferenceRange) + embeds_many(:derived_from, Epiviewpoint.R4.Reference) + embeds_many(:focus, Epiviewpoint.R4.Reference) + embeds_many(:based_on, Epiviewpoint.R4.Reference) + embeds_many(:component, Epiviewpoint.R4.Observation.Component) + embeds_many(:performer, Epiviewpoint.R4.Reference) + embeds_many(:modifier_extension, Epiviewpoint.R4.Extension) + embeds_many(:identifier, Epiviewpoint.R4.Identifier) + embeds_many(:part_of, Epiviewpoint.R4.Reference) + embeds_many(:has_member, Epiviewpoint.R4.Reference) + embeds_many(:category, Epiviewpoint.R4.CodeableConcept) + embeds_many(:note, Epiviewpoint.R4.Annotation) + embeds_many(:interpretation, Epiviewpoint.R4.CodeableConcept) + end + + def choices("effective") do + [:effective_date_time, :effective_period, :effective_timing, :effective_instant] + end + + def choices("effectivedateTime"), do: :error + + def choices("effectivePeriod"), do: :error + + def choices("effectiveTiming"), do: :error + + def choices("effectiveinstant"), do: :error + + def choices("value") do + [ + :value_quantity, + :value_codeable_concept, + :value_string, + :value_boolean, + :value_integer, + :value_range, + :value_ratio, + :value_sampled_data, + :value_time, + :value_date_time, + :value_period + ] + end + + def choices("valueQuantity"), do: :error + + def choices("valueCodeableConcept"), do: :error + + def choices("valuestring"), do: :error + + def choices("valueboolean"), do: :error + + def choices("valueinteger"), do: :error + + def choices("valueRange"), do: :error + + def choices("valueRatio"), do: :error + + def choices("valueSampledData"), do: :error + + def choices("valuetime"), do: :error + + def choices("valuedateTime"), do: :error + + def choices("valuePeriod"), do: :error + + def choices(_), do: nil + + def version_namespace, do: Epiviewpoint.R4 + def version, do: "R4" + def path, do: "/Observation" + + def changeset(data \\ %__MODULE__{}, attrs) do + data + |> cast(attrs, @fields) + |> cast_embed(:value_quantity) + |> cast_embed(:effective_timing) + |> cast_embed(:value_sampled_data) + |> cast_embed(:specimen) + |> cast_embed(:effective_period) + |> cast_embed(:value_ratio) + |> cast_embed(:encounter) + |> cast_embed(:code) + |> cast_embed(:subject) + |> cast_embed(:data_absent_reason) + |> cast_embed(:text) + |> cast_embed(:body_site) + |> cast_embed(:meta) + |> cast_embed(:value_period) + |> cast_embed(:value_range) + |> cast_embed(:method) + |> cast_embed(:device) + |> cast_embed(:value_codeable_concept) + |> cast_embed(:extension) + |> cast_embed(:contained) + |> cast_embed(:reference_range) + |> cast_embed(:derived_from) + |> cast_embed(:focus) + |> cast_embed(:based_on) + |> cast_embed(:component) + |> cast_embed(:performer) + |> cast_embed(:modifier_extension) + |> cast_embed(:identifier) + |> cast_embed(:part_of) + |> cast_embed(:has_member) + |> cast_embed(:category) + |> cast_embed(:note) + |> cast_embed(:interpretation) + |> validate_required(@required_fields) + end +end \ No newline at end of file diff --git a/lib/epiviewpoint/r4/observation/component.ex b/lib/epiviewpoint/r4/observation/component.ex new file mode 100644 index 00000000..a72301fa --- /dev/null +++ b/lib/epiviewpoint/r4/observation/component.ex @@ -0,0 +1,100 @@ +defmodule Epiviewpoint.R4.Observation.Component do + use Ecto.Schema + import Ecto.Changeset + + @fields [ + :id, + :value_boolean, + :value_date_time, + :value_integer, + :value_string, + :value_time + ] + @required_fields [] + + embedded_schema do + # Fields + field(:value_boolean, :boolean) + field(:value_date_time, :string) + field(:value_integer, :integer) + field(:value_string, :string) + field(:value_time, :string) + + # Embed One + embeds_one(:code, Epiviewpoint.R4.CodeableConcept) + embeds_one(:data_absent_reason, Epiviewpoint.R4.CodeableConcept) + embeds_one(:value_codeable_concept, Epiviewpoint.R4.CodeableConcept) + embeds_one(:value_period, Epiviewpoint.R4.Period) + embeds_one(:value_quantity, Epiviewpoint.R4.Quantity) + embeds_one(:value_range, Epiviewpoint.R4.Range) + embeds_one(:value_ratio, Epiviewpoint.R4.Ratio) + embeds_one(:value_sampled_data, Epiviewpoint.R4.SampledData) + + # Embed Many + embeds_many(:extension, Epiviewpoint.R4.Extension) + embeds_many(:interpretation, Epiviewpoint.R4.CodeableConcept) + embeds_many(:modifier_extension, Epiviewpoint.R4.Extension) + embeds_many(:reference_range, Epiviewpoint.R4.Observation.ReferenceRange) + end + + def choices("value") do + [ + :value_quantity, + :value_codeable_concept, + :value_string, + :value_boolean, + :value_integer, + :value_range, + :value_ratio, + :value_sampled_data, + :value_time, + :value_date_time, + :value_period + ] + end + + def choices("valueQuantity"), do: :error + + def choices("valueCodeableConcept"), do: :error + + def choices("valuestring"), do: :error + + def choices("valueboolean"), do: :error + + def choices("valueinteger"), do: :error + + def choices("valueRange"), do: :error + + def choices("valueRatio"), do: :error + + def choices("valueSampledData"), do: :error + + def choices("valuetime"), do: :error + + def choices("valuedateTime"), do: :error + + def choices("valuePeriod"), do: :error + + def choices(_), do: nil + + def version_namespace, do: Epiviewpoint.R4 + def version, do: "R4" + + def changeset(data \\ %__MODULE__{}, attrs) do + data + |> cast(attrs, @fields) + |> cast_embed(:code) + |> cast_embed(:data_absent_reason) + |> cast_embed(:value_codeable_concept) + |> cast_embed(:value_period) + |> cast_embed(:value_quantity) + |> cast_embed(:value_range) + |> cast_embed(:value_ratio) + |> cast_embed(:value_sampled_data) + |> cast_embed(:extension) + |> cast_embed(:interpretation) + |> cast_embed(:modifier_extension) + |> cast_embed(:reference_range) + |> validate_required(@required_fields) + end +end \ No newline at end of file diff --git a/lib/epiviewpoint/r4/observation/reference_range.ex b/lib/epiviewpoint/r4/observation/reference_range.ex new file mode 100644 index 00000000..94f11e5b --- /dev/null +++ b/lib/epiviewpoint/r4/observation/reference_range.ex @@ -0,0 +1,44 @@ +defmodule Epiviewpoint.R4.Observation.ReferenceRange do + use Ecto.Schema + import Ecto.Changeset + + @fields [ + :id, + :text + ] + @required_fields [] + + embedded_schema do + # Fields + field(:text, :string) + + # Embed One + embeds_one(:age, Epiviewpoint.R4.Range) + embeds_one(:high, Epiviewpoint.R4.Quantity) + embeds_one(:low, Epiviewpoint.R4.Quantity) + embeds_one(:type, Epiviewpoint.R4.CodeableConcept) + + # Embed Many + embeds_many(:applies_to, Epiviewpoint.R4.CodeableConcept) + embeds_many(:extension, Epiviewpoint.R4.Extension) + embeds_many(:modifier_extension, Epiviewpoint.R4.Extension) + end + + def choices(_), do: nil + + def version_namespace, do: Epiviewpoint.R4 + def version, do: "R4" + + def changeset(data \\ %__MODULE__{}, attrs) do + data + |> cast(attrs, @fields) + |> cast_embed(:age) + |> cast_embed(:high) + |> cast_embed(:low) + |> cast_embed(:type) + |> cast_embed(:applies_to) + |> cast_embed(:extension) + |> cast_embed(:modifier_extension) + |> validate_required(@required_fields) + end +end \ No newline at end of file diff --git a/lib/epiviewpoint/r4/organization.ex b/lib/epiviewpoint/r4/organization.ex new file mode 100644 index 00000000..ebb3f13c --- /dev/null +++ b/lib/epiviewpoint/r4/organization.ex @@ -0,0 +1,68 @@ +defmodule Epiviewpoint.R4.Organization do + use Ecto.Schema + import Ecto.Changeset + + @fields [ + :active, + :alias, + :id, + :implicit_rules, + :language, + :name + ] + @required_fields [] + + @primary_key {:id, :binary_id, autogenerate: true} + schema "organization" do + # Constants + field(:resource_type, :string, virtual: true, default: "Organization") + + # Fields + field(:active, :boolean) + field(:implicit_rules, :string) + field(:language, :string) + field(:name, :string) + + field(:alias, {:array, :string}) + + # Embed One + embeds_one(:meta, Epiviewpoint.R4.Meta) + embeds_one(:part_of, Epiviewpoint.R4.Reference) + embeds_one(:text, Epiviewpoint.R4.Narrative) + + # Embed Many + embeds_many(:address, Epiviewpoint.R4.Address) + embeds_many(:contact, Epiviewpoint.R4.Organization.Contact) + embeds_many(:contained, Epiviewpoint.R4.ResourceList) + embeds_many(:endpoint, Epiviewpoint.R4.Reference) + embeds_many(:extension, Epiviewpoint.R4.Extension) + embeds_many(:identifier, Epiviewpoint.R4.Identifier) + embeds_many(:modifier_extension, Epiviewpoint.R4.Extension) + embeds_many(:telecom, Epiviewpoint.R4.ContactPoint) + embeds_many(:type, Epiviewpoint.R4.CodeableConcept) + end + + def choices(_), do: nil + + def version_namespace, do: Epiviewpoint.R4 + def version, do: "R4" + def path, do: "/Organization" + + def changeset(data \\ %__MODULE__{}, attrs) do + data + |> cast(attrs, @fields) + |> cast_embed(:meta) + |> cast_embed(:part_of) + |> cast_embed(:text) + |> cast_embed(:address) + |> cast_embed(:contact) + |> cast_embed(:contained) + |> cast_embed(:endpoint) + |> cast_embed(:extension) + |> cast_embed(:identifier) + |> cast_embed(:modifier_extension) + |> cast_embed(:telecom) + |> cast_embed(:type) + |> validate_required(@required_fields) + end +end \ No newline at end of file diff --git a/lib/epiviewpoint/r4/organization/contact.ex b/lib/epiviewpoint/r4/organization/contact.ex new file mode 100644 index 00000000..a1ba4fa3 --- /dev/null +++ b/lib/epiviewpoint/r4/organization/contact.ex @@ -0,0 +1,38 @@ +defmodule Epiviewpoint.R4.Organization.Contact do + use Ecto.Schema + import Ecto.Changeset + + @fields [ + :id + ] + @required_fields [] + + embedded_schema do + # Embed One + embeds_one(:address, Epiviewpoint.R4.Address) + embeds_one(:name, Epiviewpoint.R4.HumanName) + embeds_one(:purpose, Epiviewpoint.R4.CodeableConcept) + + # Embed Many + embeds_many(:extension, Epiviewpoint.R4.Extension) + embeds_many(:modifier_extension, Epiviewpoint.R4.Extension) + embeds_many(:telecom, Epiviewpoint.R4.ContactPoint) + end + + def choices(_), do: nil + + def version_namespace, do: Epiviewpoint.R4 + def version, do: "R4" + + def changeset(data \\ %__MODULE__{}, attrs) do + data + |> cast(attrs, @fields) + |> cast_embed(:address) + |> cast_embed(:name) + |> cast_embed(:purpose) + |> cast_embed(:extension) + |> cast_embed(:modifier_extension) + |> cast_embed(:telecom) + |> validate_required(@required_fields) + end +end \ No newline at end of file diff --git a/lib/epiviewpoint/r4/parameter_definition.ex b/lib/epiviewpoint/r4/parameter_definition.ex new file mode 100644 index 00000000..6c4bd521 --- /dev/null +++ b/lib/epiviewpoint/r4/parameter_definition.ex @@ -0,0 +1,42 @@ +defmodule Epiviewpoint.R4.ParameterDefinition do + use Ecto.Schema + import Ecto.Changeset + + @fields [ + :documentation, + :id, + :max, + :min, + :name, + :profile, + :type, + :use + ] + @required_fields [] + + embedded_schema do + # Fields + field(:documentation, :string) + field(:max, :string) + field(:min, :integer) + field(:name, :string) + field(:profile, :string) + field(:type, :string) + field(:use, :string) + + # Embed Many + embeds_many(:extension, Epiviewpoint.R4.Extension) + end + + def choices(_), do: nil + + def version_namespace, do: Epiviewpoint.R4 + def version, do: "R4" + + def changeset(data \\ %__MODULE__{}, attrs) do + data + |> cast(attrs, @fields) + |> cast_embed(:extension) + |> validate_required(@required_fields) + end +end \ No newline at end of file diff --git a/lib/epiviewpoint/r4/patient.ex b/lib/epiviewpoint/r4/patient.ex new file mode 100644 index 00000000..e760829b --- /dev/null +++ b/lib/epiviewpoint/r4/patient.ex @@ -0,0 +1,108 @@ +defmodule Epiviewpoint.R4.Patient do + use Ecto.Schema + import Ecto.Changeset + + @fields [ + :active, + :multiple_birth_boolean, + :language, + :implicit_rules, + :birth_date, + :multiple_birth_integer, + :id, + :deceased_boolean, + :gender, + :deceased_date_time + ] + @required_fields [] + + @primary_key {:id, :binary_id, autogenerate: true} + schema "patient" do + # Constants + field(:resource_type, :string, virtual: true, default: "Patient") + + # Fields + field(:active, :boolean) + field(:multiple_birth_boolean, :boolean) + field(:language, :string) + field(:implicit_rules, :string) + field(:birth_date, :date) + field(:multiple_birth_integer, :integer) + field(:deceased_boolean, :boolean) + field(:deceased_date_time, :string) + + # Enum + field(:gender, Ecto.Enum, + values: [ + :male, + :female, + :other, + :unknown + ] + ) + + # Embed One + embeds_one(:marital_status, Epiviewpoint.R4.CodeableConcept) + embeds_one(:managing_organization, Epiviewpoint.R4.Reference) + embeds_one(:text, Epiviewpoint.R4.Narrative) + embeds_one(:meta, Epiviewpoint.R4.Meta) + + # Embed Many + embeds_many(:photo, Epiviewpoint.R4.Attachment) + embeds_many(:communication, Epiviewpoint.R4.Patient.Communication) + embeds_many(:name, Epiviewpoint.R4.HumanName) + embeds_many(:extension, Epiviewpoint.R4.Extension) + embeds_many(:telecom, Epiviewpoint.R4.ContactPoint) + embeds_many(:contained, Epiviewpoint.R4.ResourceList) + embeds_many(:link, Epiviewpoint.R4.Patient.Link) + embeds_many(:contact, Epiviewpoint.R4.Patient.Contact) + embeds_many(:modifier_extension, Epiviewpoint.R4.Extension) + embeds_many(:identifier, Epiviewpoint.R4.Identifier) + embeds_many(:general_practitioner, Epiviewpoint.R4.Reference) + embeds_many(:address, Epiviewpoint.R4.Address) + end + + def choices("deceased") do + [:deceased_boolean, :deceased_date_time] + end + + def choices("deceasedboolean"), do: :error + + def choices("deceaseddateTime"), do: :error + + def choices("multipleBirth") do + [:multipleBirth_boolean, :multipleBirth_integer] + end + + def choices("multipleBirthboolean"), do: :error + + def choices("multipleBirthinteger"), do: :error + + def choices(_), do: nil + + def version_namespace, do: Epiviewpoint.R4 + def version, do: "R4" + def path, do: "/Patient" + + def changeset(data \\ %__MODULE__{}, attrs) do + data + |> cast(attrs, @fields) + |> cast_embed(:marital_status) + |> cast_embed(:managing_organization) + |> cast_embed(:text) + |> cast_embed(:meta) + |> cast_embed(:photo) + |> cast_embed(:communication) + |> cast_embed(:name) + |> cast_embed(:extension) + |> cast_embed(:telecom) + |> cast_embed(:contained) + |> cast_embed(:link) + |> cast_embed(:contact) + |> cast_embed(:modifier_extension) + |> cast_embed(:identifier) + |> cast_embed(:general_practitioner) + |> cast_embed(:address) + |> validate_required(@required_fields) + end +end \ No newline at end of file diff --git a/lib/epiviewpoint/r4/patient/communication.ex b/lib/epiviewpoint/r4/patient/communication.ex new file mode 100644 index 00000000..11aa016b --- /dev/null +++ b/lib/epiviewpoint/r4/patient/communication.ex @@ -0,0 +1,36 @@ +defmodule Epiviewpoint.R4.Patient.Communication do + use Ecto.Schema + import Ecto.Changeset + + @fields [ + :id, + :preferred + ] + @required_fields [] + + embedded_schema do + # Fields + field(:preferred, :boolean) + + # Embed One + embeds_one(:language, Epiviewpoint.R4.CodeableConcept) + + # Embed Many + embeds_many(:extension, Epiviewpoint.R4.Extension) + embeds_many(:modifier_extension, Epiviewpoint.R4.Extension) + end + + def choices(_), do: nil + + def version_namespace, do: Epiviewpoint.R4 + def version, do: "R4" + + def changeset(data \\ %__MODULE__{}, attrs) do + data + |> cast(attrs, @fields) + |> cast_embed(:language) + |> cast_embed(:extension) + |> cast_embed(:modifier_extension) + |> validate_required(@required_fields) + end +end \ No newline at end of file diff --git a/lib/epiviewpoint/r4/patient/contact.ex b/lib/epiviewpoint/r4/patient/contact.ex new file mode 100644 index 00000000..d552e4fa --- /dev/null +++ b/lib/epiviewpoint/r4/patient/contact.ex @@ -0,0 +1,53 @@ +defmodule Epiviewpoint.R4.Patient.Contact do + use Ecto.Schema + import Ecto.Changeset + + @fields [ + :gender, + :id + ] + @required_fields [] + + embedded_schema do + # Enum + field(:gender, Ecto.Enum, + values: [ + :male, + :female, + :other, + :unknown + ] + ) + + # Embed One + embeds_one(:address, Epiviewpoint.R4.Address) + embeds_one(:name, Epiviewpoint.R4.HumanName) + embeds_one(:organization, Epiviewpoint.R4.Reference) + embeds_one(:period, Epiviewpoint.R4.Period) + + # Embed Many + embeds_many(:extension, Epiviewpoint.R4.Extension) + embeds_many(:modifier_extension, Epiviewpoint.R4.Extension) + embeds_many(:relationship, Epiviewpoint.R4.CodeableConcept) + embeds_many(:telecom, Epiviewpoint.R4.ContactPoint) + end + + def choices(_), do: nil + + def version_namespace, do: Epiviewpoint.R4 + def version, do: "R4" + + def changeset(data \\ %__MODULE__{}, attrs) do + data + |> cast(attrs, @fields) + |> cast_embed(:address) + |> cast_embed(:name) + |> cast_embed(:organization) + |> cast_embed(:period) + |> cast_embed(:extension) + |> cast_embed(:modifier_extension) + |> cast_embed(:relationship) + |> cast_embed(:telecom) + |> validate_required(@required_fields) + end +end \ No newline at end of file diff --git a/lib/epiviewpoint/r4/patient/link.ex b/lib/epiviewpoint/r4/patient/link.ex new file mode 100644 index 00000000..6aa952d1 --- /dev/null +++ b/lib/epiviewpoint/r4/patient/link.ex @@ -0,0 +1,43 @@ +defmodule Epiviewpoint.R4.Patient.Link do + use Ecto.Schema + import Ecto.Changeset + + @fields [ + :id, + :type + ] + @required_fields [] + + embedded_schema do + # Enum + field(:type, Ecto.Enum, + values: [ + :replaced_by, + :replaces, + :refer, + :seealso + ] + ) + + # Embed One + embeds_one(:other, Epiviewpoint.R4.Reference) + + # Embed Many + embeds_many(:extension, Epiviewpoint.R4.Extension) + embeds_many(:modifier_extension, Epiviewpoint.R4.Extension) + end + + def choices(_), do: nil + + def version_namespace, do: Epiviewpoint.R4 + def version, do: "R4" + + def changeset(data \\ %__MODULE__{}, attrs) do + data + |> cast(attrs, @fields) + |> cast_embed(:other) + |> cast_embed(:extension) + |> cast_embed(:modifier_extension) + |> validate_required(@required_fields) + end +end \ No newline at end of file diff --git a/lib/epiviewpoint/r4/period.ex b/lib/epiviewpoint/r4/period.ex new file mode 100644 index 00000000..b4de40d4 --- /dev/null +++ b/lib/epiviewpoint/r4/period.ex @@ -0,0 +1,32 @@ +defmodule Epiviewpoint.R4.Period do + use Ecto.Schema + import Ecto.Changeset + + @fields [ + :end, + :id, + :start + ] + @required_fields [] + + embedded_schema do + # Fields + field(:end, :utc_datetime_usec) + field(:start, :utc_datetime_usec) + + # Embed Many + embeds_many(:extension, Epiviewpoint.R4.Extension) + end + + def choices(_), do: nil + + def version_namespace, do: Epiviewpoint.R4 + def version, do: "R4" + + def changeset(data \\ %__MODULE__{}, attrs) do + data + |> cast(attrs, @fields) + |> cast_embed(:extension) + |> validate_required(@required_fields) + end +end \ No newline at end of file diff --git a/lib/epiviewpoint/r4/quantity.ex b/lib/epiviewpoint/r4/quantity.ex new file mode 100644 index 00000000..22aab8b5 --- /dev/null +++ b/lib/epiviewpoint/r4/quantity.ex @@ -0,0 +1,47 @@ +defmodule Epiviewpoint.R4.Quantity do + use Ecto.Schema + import Ecto.Changeset + + @fields [ + :code, + :comparator, + :id, + :system, + :unit, + :value + ] + @required_fields [] + + embedded_schema do + # Fields + field(:code, :string) + field(:system, :string) + field(:unit, :string) + field(:value, :decimal) + + # Enum + field(:comparator, Ecto.Enum, + values: [ + :<, + :<=, + :>=, + :> + ] + ) + + # Embed Many + embeds_many(:extension, Epiviewpoint.R4.Extension) + end + + def choices(_), do: nil + + def version_namespace, do: Epiviewpoint.R4 + def version, do: "R4" + + def changeset(data \\ %__MODULE__{}, attrs) do + data + |> cast(attrs, @fields) + |> cast_embed(:extension) + |> validate_required(@required_fields) + end +end \ No newline at end of file diff --git a/lib/epiviewpoint/r4/range.ex b/lib/epiviewpoint/r4/range.ex new file mode 100644 index 00000000..d51e9c8e --- /dev/null +++ b/lib/epiviewpoint/r4/range.ex @@ -0,0 +1,32 @@ +defmodule Epiviewpoint.R4.Range do + use Ecto.Schema + import Ecto.Changeset + + @fields [ + :id + ] + @required_fields [] + + embedded_schema do + # Embed One + embeds_one(:high, Epiviewpoint.R4.Quantity) + embeds_one(:low, Epiviewpoint.R4.Quantity) + + # Embed Many + embeds_many(:extension, Epiviewpoint.R4.Extension) + end + + def choices(_), do: nil + + def version_namespace, do: Epiviewpoint.R4 + def version, do: "R4" + + def changeset(data \\ %__MODULE__{}, attrs) do + data + |> cast(attrs, @fields) + |> cast_embed(:high) + |> cast_embed(:low) + |> cast_embed(:extension) + |> validate_required(@required_fields) + end +end \ No newline at end of file diff --git a/lib/epiviewpoint/r4/ratio.ex b/lib/epiviewpoint/r4/ratio.ex new file mode 100644 index 00000000..50a3790f --- /dev/null +++ b/lib/epiviewpoint/r4/ratio.ex @@ -0,0 +1,32 @@ +defmodule Epiviewpoint.R4.Ratio do + use Ecto.Schema + import Ecto.Changeset + + @fields [ + :id + ] + @required_fields [] + + embedded_schema do + # Embed One + embeds_one(:denominator, Epiviewpoint.R4.Quantity) + embeds_one(:numerator, Epiviewpoint.R4.Quantity) + + # Embed Many + embeds_many(:extension, Epiviewpoint.R4.Extension) + end + + def choices(_), do: nil + + def version_namespace, do: Epiviewpoint.R4 + def version, do: "R4" + + def changeset(data \\ %__MODULE__{}, attrs) do + data + |> cast(attrs, @fields) + |> cast_embed(:denominator) + |> cast_embed(:numerator) + |> cast_embed(:extension) + |> validate_required(@required_fields) + end +end \ No newline at end of file diff --git a/lib/epiviewpoint/r4/reference.ex b/lib/epiviewpoint/r4/reference.ex new file mode 100644 index 00000000..92b83c7b --- /dev/null +++ b/lib/epiviewpoint/r4/reference.ex @@ -0,0 +1,38 @@ +defmodule Epiviewpoint.R4.Reference do + use Ecto.Schema + import Ecto.Changeset + + @fields [ + :display, + :id, + :reference, + :type + ] + @required_fields [] + + embedded_schema do + # Fields + field(:display, :string) + field(:reference, :string) + field(:type, :string) + + # Embed One + embeds_one(:identifier, Epiviewpoint.R4.Identifier) + + # Embed Many + embeds_many(:extension, Epiviewpoint.R4.Extension) + end + + def choices(_), do: nil + + def version_namespace, do: Epiviewpoint.R4 + def version, do: "R4" + + def changeset(data \\ %__MODULE__{}, attrs) do + data + |> cast(attrs, @fields) + |> cast_embed(:identifier) + |> cast_embed(:extension) + |> validate_required(@required_fields) + end +end \ No newline at end of file diff --git a/lib/epiviewpoint/r4/related_artifact.ex b/lib/epiviewpoint/r4/related_artifact.ex new file mode 100644 index 00000000..faabd573 --- /dev/null +++ b/lib/epiviewpoint/r4/related_artifact.ex @@ -0,0 +1,57 @@ +defmodule Epiviewpoint.R4.RelatedArtifact do + use Ecto.Schema + import Ecto.Changeset + + @fields [ + :citation, + :display, + :id, + :label, + :resource, + :type, + :url + ] + @required_fields [] + + embedded_schema do + # Fields + field(:citation, :string) + field(:display, :string) + field(:label, :string) + field(:resource, :string) + field(:url, :string) + + # Enum + field(:type, Ecto.Enum, + values: [ + :documentation, + :justification, + :citation, + :predecessor, + :successor, + :derived_from, + :depends_on, + :composed_of + ] + ) + + # Embed One + embeds_one(:document, Epiviewpoint.R4.Attachment) + + # Embed Many + embeds_many(:extension, Epiviewpoint.R4.Extension) + end + + def choices(_), do: nil + + def version_namespace, do: Epiviewpoint.R4 + def version, do: "R4" + + def changeset(data \\ %__MODULE__{}, attrs) do + data + |> cast(attrs, @fields) + |> cast_embed(:document) + |> cast_embed(:extension) + |> validate_required(@required_fields) + end +end \ No newline at end of file diff --git a/lib/epiviewpoint/r4/resource_list.ex b/lib/epiviewpoint/r4/resource_list.ex new file mode 100644 index 00000000..1fb62b10 --- /dev/null +++ b/lib/epiviewpoint/r4/resource_list.ex @@ -0,0 +1,21 @@ +defmodule Epiviewpoint.R4.ResourceList do + use Ecto.Schema + import Ecto.Changeset + + @fields [] + @required_fields [] + + embedded_schema do + end + + def choices(_), do: nil + + def version_namespace, do: Epiviewpoint.R4 + def version, do: "R4" + + def changeset(data \\ %__MODULE__{}, attrs) do + data + |> cast(attrs, @fields) + |> validate_required(@required_fields) + end +end \ No newline at end of file diff --git a/lib/epiviewpoint/r4/sampled_data.ex b/lib/epiviewpoint/r4/sampled_data.ex new file mode 100644 index 00000000..4ee45d48 --- /dev/null +++ b/lib/epiviewpoint/r4/sampled_data.ex @@ -0,0 +1,44 @@ +defmodule Epiviewpoint.R4.SampledData do + use Ecto.Schema + import Ecto.Changeset + + @fields [ + :data, + :dimensions, + :factor, + :id, + :lower_limit, + :period, + :upper_limit + ] + @required_fields [] + + embedded_schema do + # Fields + field(:data, :string) + field(:dimensions, :integer) + field(:factor, :decimal) + field(:lower_limit, :decimal) + field(:period, :decimal) + field(:upper_limit, :decimal) + + # Embed One + embeds_one(:origin, Epiviewpoint.R4.Quantity) + + # Embed Many + embeds_many(:extension, Epiviewpoint.R4.Extension) + end + + def choices(_), do: nil + + def version_namespace, do: Epiviewpoint.R4 + def version, do: "R4" + + def changeset(data \\ %__MODULE__{}, attrs) do + data + |> cast(attrs, @fields) + |> cast_embed(:origin) + |> cast_embed(:extension) + |> validate_required(@required_fields) + end +end \ No newline at end of file diff --git a/lib/epiviewpoint/r4/signature.ex b/lib/epiviewpoint/r4/signature.ex new file mode 100644 index 00000000..7dd9298d --- /dev/null +++ b/lib/epiviewpoint/r4/signature.ex @@ -0,0 +1,44 @@ +defmodule Epiviewpoint.R4.Signature do + use Ecto.Schema + import Ecto.Changeset + + @fields [ + :data, + :id, + :sig_format, + :target_format, + :when + ] + @required_fields [] + + embedded_schema do + # Fields + field(:data, :string) + field(:sig_format, :string) + field(:target_format, :string) + field(:when, :utc_datetime_usec) + + # Embed One + embeds_one(:on_behalf_of, Epiviewpoint.R4.Reference) + embeds_one(:who, Epiviewpoint.R4.Reference) + + # Embed Many + embeds_many(:extension, Epiviewpoint.R4.Extension) + embeds_many(:type, Epiviewpoint.R4.Coding) + end + + def choices(_), do: nil + + def version_namespace, do: Epiviewpoint.R4 + def version, do: "R4" + + def changeset(data \\ %__MODULE__{}, attrs) do + data + |> cast(attrs, @fields) + |> cast_embed(:on_behalf_of) + |> cast_embed(:who) + |> cast_embed(:extension) + |> cast_embed(:type) + |> validate_required(@required_fields) + end +end \ No newline at end of file diff --git a/lib/epiviewpoint/r4/timing.ex b/lib/epiviewpoint/r4/timing.ex new file mode 100644 index 00000000..77524b79 --- /dev/null +++ b/lib/epiviewpoint/r4/timing.ex @@ -0,0 +1,37 @@ +defmodule Epiviewpoint.R4.Timing do + use Ecto.Schema + import Ecto.Changeset + + @fields [ + :event, + :id + ] + @required_fields [] + + embedded_schema do + field(:event, {:array, :utc_datetime_usec}) + + # Embed One + embeds_one(:code, Epiviewpoint.R4.CodeableConcept) + embeds_one(:repeat, Epiviewpoint.R4.Timing.Repeat) + + # Embed Many + embeds_many(:extension, Epiviewpoint.R4.Extension) + embeds_many(:modifier_extension, Epiviewpoint.R4.Extension) + end + + def choices(_), do: nil + + def version_namespace, do: Epiviewpoint.R4 + def version, do: "R4" + + def changeset(data \\ %__MODULE__{}, attrs) do + data + |> cast(attrs, @fields) + |> cast_embed(:code) + |> cast_embed(:repeat) + |> cast_embed(:extension) + |> cast_embed(:modifier_extension) + |> validate_required(@required_fields) + end +end \ No newline at end of file diff --git a/lib/epiviewpoint/r4/timing/repeat.ex b/lib/epiviewpoint/r4/timing/repeat.ex new file mode 100644 index 00000000..321f3071 --- /dev/null +++ b/lib/epiviewpoint/r4/timing/repeat.ex @@ -0,0 +1,100 @@ +defmodule Epiviewpoint.R4.Timing.Repeat do + use Ecto.Schema + import Ecto.Changeset + + @fields [ + :day_of_week, + :duration_unit, + :count, + :count_max, + :period_unit, + :when, + :frequency, + :period_max, + :duration_max, + :time_of_day, + :duration, + :frequency_max, + :offset, + :period, + :id + ] + @required_fields [] + + embedded_schema do + # Fields + field(:count, :integer) + field(:count_max, :integer) + field(:frequency, :integer) + field(:period_max, :decimal) + field(:duration_max, :decimal) + field(:duration, :decimal) + field(:frequency_max, :integer) + field(:offset, :integer) + field(:period, :decimal) + + field(:day_of_week, {:array, :string}) + field(:when, {:array, :string}) + field(:time_of_day, {:array, :time_usec}) + + # Enum + field(:duration_unit, Ecto.Enum, + values: [ + :s, + :min, + :h, + :d, + :wk, + :mo, + :a + ] + ) + + field(:period_unit, Ecto.Enum, + values: [ + :s, + :min, + :h, + :d, + :wk, + :mo, + :a + ] + ) + + # Embed One + embeds_one(:bounds_duration, Epiviewpoint.R4.Duration) + embeds_one(:bounds_range, Epiviewpoint.R4.Range) + embeds_one(:bounds_period, Epiviewpoint.R4.Period) + + # Embed Many + embeds_many(:extension, Epiviewpoint.R4.Extension) + embeds_many(:modifier_extension, Epiviewpoint.R4.Extension) + end + + def choices("bounds") do + [:bounds_duration, :bounds_range, :bounds_period] + end + + def choices("boundsDuration"), do: :error + + def choices("boundsRange"), do: :error + + def choices("boundsPeriod"), do: :error + + def choices(_), do: nil + + def version_namespace, do: Epiviewpoint.R4 + def version, do: "R4" + + def changeset(data \\ %__MODULE__{}, attrs) do + data + |> cast(attrs, @fields) + |> cast_embed(:bounds_duration) + |> cast_embed(:bounds_range) + |> cast_embed(:bounds_period) + |> cast_embed(:extension) + |> cast_embed(:modifier_extension) + |> validate_required(@required_fields) + end +end \ No newline at end of file diff --git a/lib/epiviewpoint/r4/trigger_definition.ex b/lib/epiviewpoint/r4/trigger_definition.ex new file mode 100644 index 00000000..febc5075 --- /dev/null +++ b/lib/epiviewpoint/r4/trigger_definition.ex @@ -0,0 +1,71 @@ +defmodule Epiviewpoint.R4.TriggerDefinition do + use Ecto.Schema + import Ecto.Changeset + + @fields [ + :id, + :name, + :timing_date, + :timing_date_time, + :type + ] + @required_fields [] + + embedded_schema do + # Fields + field(:name, :string) + field(:timing_date, :string) + field(:timing_date_time, :string) + + # Enum + field(:type, Ecto.Enum, + values: [ + :named_event, + :periodic, + :data_changed, + :data_added, + :data_modified, + :data_removed, + :data_accessed, + :data_access_ended + ] + ) + + # Embed One + embeds_one(:condition, Epiviewpoint.R4.Expression) + embeds_one(:timing_reference, Epiviewpoint.R4.Reference) + embeds_one(:timing_timing, Epiviewpoint.R4.Timing) + + # Embed Many + embeds_many(:data, Epiviewpoint.R4.DataRequirement) + embeds_many(:extension, Epiviewpoint.R4.Extension) + end + + def choices("timing") do + [:timing_timing, :timing_reference, :timing_date, :timing_date_time] + end + + def choices("timingTiming"), do: :error + + def choices("timingReference"), do: :error + + def choices("timingdate"), do: :error + + def choices("timingdateTime"), do: :error + + def choices(_), do: nil + + def version_namespace, do: Epiviewpoint.R4 + def version, do: "R4" + + def changeset(data \\ %__MODULE__{}, attrs) do + data + |> cast(attrs, @fields) + |> cast_embed(:condition) + |> cast_embed(:timing_reference) + |> cast_embed(:timing_timing) + |> cast_embed(:data) + |> cast_embed(:extension) + |> validate_required(@required_fields) + end +end \ No newline at end of file diff --git a/lib/epiviewpoint/r4/usage_context.ex b/lib/epiviewpoint/r4/usage_context.ex new file mode 100644 index 00000000..b96c15a6 --- /dev/null +++ b/lib/epiviewpoint/r4/usage_context.ex @@ -0,0 +1,50 @@ +defmodule Epiviewpoint.R4.UsageContext do + use Ecto.Schema + import Ecto.Changeset + + @fields [ + :id + ] + @required_fields [] + + embedded_schema do + # Embed One + embeds_one(:code, Epiviewpoint.R4.Coding) + embeds_one(:value_codeable_concept, Epiviewpoint.R4.CodeableConcept) + embeds_one(:value_quantity, Epiviewpoint.R4.Quantity) + embeds_one(:value_range, Epiviewpoint.R4.Range) + embeds_one(:value_reference, Epiviewpoint.R4.Reference) + + # Embed Many + embeds_many(:extension, Epiviewpoint.R4.Extension) + end + + def choices("value") do + [:value_codeable_concept, :value_quantity, :value_range, :value_reference] + end + + def choices("valueCodeableConcept"), do: :error + + def choices("valueQuantity"), do: :error + + def choices("valueRange"), do: :error + + def choices("valueReference"), do: :error + + def choices(_), do: nil + + def version_namespace, do: Epiviewpoint.R4 + def version, do: "R4" + + def changeset(data \\ %__MODULE__{}, attrs) do + data + |> cast(attrs, @fields) + |> cast_embed(:code) + |> cast_embed(:value_codeable_concept) + |> cast_embed(:value_quantity) + |> cast_embed(:value_range) + |> cast_embed(:value_reference) + |> cast_embed(:extension) + |> validate_required(@required_fields) + end +end \ No newline at end of file diff --git a/lib/epiviewpoint_web/controllers/import_controller.ex b/lib/epiviewpoint_web/controllers/import_controller.ex index 20706d19..a8efc1c7 100644 --- a/lib/epiviewpoint_web/controllers/import_controller.ex +++ b/lib/epiviewpoint_web/controllers/import_controller.ex @@ -41,6 +41,32 @@ defmodule EpiViewpointWeb.ImportController do end end + def create_bulk_fhir(conn, %{"files" => plug_uploads}) do + + result = UploadedFile.from_plug_uploads(plug_uploads) |> Cases.import_bulk_fhir_lab_results(conn.assigns.current_user) + + case result do + {:ok, import_info} -> + {_imported_people, popped_import_info} = import_info |> Map.pop(:imported_people) + + conn + |> Session.set_last_file_import_info(popped_import_info) + |> redirect(to: ~p"/import/complete") + + {:error, [user_readable: user_readable_message]} -> + conn + |> Session.set_import_error_message(user_readable_message) + |> redirect(to: ~p"/import/start") + + {:error, %DateParsingError{user_readable: user_readable_message}} -> + conn + |> Session.set_import_error_message(user_readable_message) + |> redirect(to: ~p"/import/start") + end + + end + + def show(conn, _params) do conn |> assign_defaults(@common_assigns) diff --git a/lib/epiviewpoint_web/live/import_live.html.heex b/lib/epiviewpoint_web/live/import_live.html.heex index 59c83bdf..96ff9085 100644 --- a/lib/epiviewpoint_web/live/import_live.html.heex +++ b/lib/epiviewpoint_web/live/import_live.html.heex @@ -14,4 +14,13 @@ type="submit" value="Upload" /> +

+ Choose bulk FHIR NDJSON files from your computer, click “Upload”, and then wait for the file to upload. +

+ <%= form_tag("/import/upload_bulk_fhir", multipart: true, method: :post) %> diff --git a/lib/epiviewpoint_web/router.ex b/lib/epiviewpoint_web/router.ex index 44e0b9df..af77d70f 100644 --- a/lib/epiviewpoint_web/router.ex +++ b/lib/epiviewpoint_web/router.ex @@ -56,6 +56,7 @@ defmodule EpiViewpointWeb.Router do live "/import/start", ImportLive, as: :import_start get "/import/complete", ImportController, :show post "/import/upload", ImportController, :create + post "/import/upload_bulk_fhir", ImportController, :create_bulk_fhir live "/case-investigations/:case_investigation_id/contact", CaseInvestigationContactLive, as: :create_case_investigation_contact live "/case-investigations/:case_investigation_id/contact/:id", CaseInvestigationContactLive, as: :edit_case_investigation_contact diff --git a/lib/epiviewpoint_web/uploaded_file.ex b/lib/epiviewpoint_web/uploaded_file.ex index 70a44c20..1967fed0 100644 --- a/lib/epiviewpoint_web/uploaded_file.ex +++ b/lib/epiviewpoint_web/uploaded_file.ex @@ -3,4 +3,8 @@ defmodule EpiViewpointWeb.UploadedFile do def from_plug_upload(%{path: path, filename: file_name}) do %{file_name: Zarex.sanitize(file_name), contents: File.read!(path)} end + + def from_plug_uploads(plug_uploads) do + Enum.map(plug_uploads, &from_plug_upload/1) + end end diff --git a/mix.exs b/mix.exs index ca4885e2..a9fa3dd9 100644 --- a/mix.exs +++ b/mix.exs @@ -56,6 +56,7 @@ defmodule EpiViewpoint.MixProject do {:gettext, "~> 0.11"}, {:inflex, "~> 2.1"}, {:jason, "~> 1.0"}, + {:kindling, "~> 1.0.1"}, {:logger_json, "~> 4.3"}, {:mix_audit, "~> 1.0", runtime: false}, {:mix_test_watch, "~> 1.0", only: :dev, runtime: false}, diff --git a/mix.lock b/mix.lock index 2467b680..07177a7a 100644 --- a/mix.lock +++ b/mix.lock @@ -20,21 +20,28 @@ "explorer": {:hex, :explorer, "0.9.1", "9c6f175dfd2fa2f432d5fe9a86b81875438a9a1110af5b952c284842bee434e4", [:mix], [{:adbc, "~> 0.1", [hex: :adbc, repo: "hexpm", optional: true]}, {:aws_signature, "~> 0.3", [hex: :aws_signature, repo: "hexpm", optional: false]}, {:castore, "~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:flame, "~> 0.3", [hex: :flame, repo: "hexpm", optional: true]}, {:fss, "~> 0.1", [hex: :fss, repo: "hexpm", optional: false]}, {:nx, "~> 0.4", [hex: :nx, repo: "hexpm", optional: true]}, {:rustler, "~> 0.34.0", [hex: :rustler, repo: "hexpm", optional: true]}, {:rustler_precompiled, "~> 0.7", [hex: :rustler_precompiled, repo: "hexpm", optional: false]}, {:table, "~> 0.1.2", [hex: :table, repo: "hexpm", optional: false]}, {:table_rex, "~> 3.1.1 or ~> 4.0.0", [hex: :table_rex, repo: "hexpm", optional: false]}], "hexpm", "d88ec0e78f904c5eaf0b37c4a0ce4632de133515f3740a29fbddd2c0d0a78e77"}, "expo": {:hex, :expo, "0.4.1", "1c61d18a5df197dfda38861673d392e642649a9cef7694d2f97a587b2cfb319b", [:mix], [], "hexpm", "2ff7ba7a798c8c543c12550fa0e2cbc81b95d4974c65855d8d15ba7b37a1ce47"}, "file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"}, + "finch": {:hex, :finch, "0.19.0", "c644641491ea854fc5c1bbaef36bfc764e3f08e7185e1f084e35e0672241b76d", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.6.2 or ~> 1.7", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 1.1", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "fc5324ce209125d1e2fa0fcd2634601c52a787aff1cd33ee833664a5af4ea2b6"}, "floki": {:hex, :floki, "0.36.2", "a7da0193538c93f937714a6704369711998a51a6164a222d710ebd54020aa7a3", [:mix], [], "hexpm", "a8766c0bc92f074e5cb36c4f9961982eda84c5d2b8e979ca67f5c268ec8ed580"}, "fss": {:hex, :fss, "0.1.1", "9db2344dbbb5d555ce442ac7c2f82dd975b605b50d169314a20f08ed21e08642", [:mix], [], "hexpm", "78ad5955c7919c3764065b21144913df7515d52e228c09427a004afe9c1a16b0"}, "gettext": {:hex, :gettext, "0.23.1", "821e619a240e6000db2fc16a574ef68b3bd7fe0167ccc264a81563cc93e67a31", [:mix], [{:expo, "~> 0.4.0", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "19d744a36b809d810d610b57c27b934425859d158ebd56561bc41f7eeb8795db"}, "hackney": {:hex, :hackney, "1.18.0", "c4443d960bb9fba6d01161d01cd81173089686717d9490e5d3606644c48d121f", [:rebar3], [{:certifi, "~>2.8.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~>6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~>1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~>1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "9afcda620704d720db8c6a3123e9848d09c87586dc1c10479c42627b905b5c5e"}, + "hpax": {:hex, :hpax, "1.0.0", "28dcf54509fe2152a3d040e4e3df5b265dcb6cb532029ecbacf4ce52caea3fd2", [:mix], [], "hexpm", "7f1314731d711e2ca5fdc7fd361296593fc2542570b3105595bb0bc6d0fad601"}, "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"}, "inflex": {:hex, :inflex, "2.1.0", "a365cf0821a9dacb65067abd95008ca1b0bb7dcdd85ae59965deef2aa062924c", [:mix], [], "hexpm", "14c17d05db4ee9b6d319b0bff1bdf22aa389a25398d1952c7a0b5f3d93162dd8"}, "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"}, + "kindling": {:hex, :kindling, "1.0.1", "1ddab6923d66ae400b5a4f130391546b6d3245ee072fbda4d3279e8708db0f1d", [:mix], [{:ecto, "~> 3.11", [hex: :ecto, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:recase, "~> 0.7.0", [hex: :recase, repo: "hexpm", optional: false]}, {:req, "~> 0.4.11", [hex: :req, repo: "hexpm", optional: false]}, {:saxy, "~> 1.5.0", [hex: :saxy, repo: "hexpm", optional: false]}], "hexpm", "8e9831e1a5ce063cfa8332e4bf34899380f5eb540d89b04fc38314443cdd016a"}, "logger_json": {:hex, :logger_json, "4.3.0", "41aaaab2c2e1c071bfddbcc5a3f567884fdf312d222c7f1a7e3de6ab667774f7", [:mix], [{:ecto, "~> 2.1 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "001bbc34d7c451cfeed298c8384cb3aab10b364db2eb095c466c7a1a28bee6e0"}, "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"}, "mime": {:hex, :mime, "2.0.6", "8f18486773d9b15f95f4f4f1e39b710045fa1de891fada4516559967276e4dc2", [:mix], [], "hexpm", "c9945363a6b26d747389aac3643f8e0e09d30499a138ad64fe8fd1d13d9b153e"}, "mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"}, + "mint": {:hex, :mint, "1.6.2", "af6d97a4051eee4f05b5500671d47c3a67dac7386045d87a904126fd4bbcea2e", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0 or ~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "5ee441dffc1892f1ae59127f74afe8fd82fda6587794278d924e4d90ea3d63f9"}, "mix_audit": {:hex, :mix_audit, "1.0.0", "d2b5adbd69f34ba6b5b7d52812b1ba06f9110367e196d3ba5dba7753124cf8be", [:make, :mix], [{:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:yaml_elixir, "~> 2.8.0", [hex: :yaml_elixir, repo: "hexpm", optional: false]}], "hexpm", "1b1ff6694e6eb12818ce5dcc276a39bbe03e27fcd11376c381bfe6b4900f2aa8"}, "mix_test_watch": {:hex, :mix_test_watch, "1.1.0", "330bb91c8ed271fe408c42d07e0773340a7938d8a0d281d57a14243eae9dc8c3", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm", "52b6b1c476cbb70fd899ca5394506482f12e5f6b0d6acff9df95c7f1e0812ec3"}, "mox": {:hex, :mox, "1.0.1", "b651bf0113265cda0ba3a827fcb691f848b683c373b77e7d7439910a8d754d6e", [:mix], [], "hexpm", "35bc0dea5499d18db4ef7fe4360067a59b06c74376eb6ab3bd67e6295b133469"}, "nimble_csv": {:hex, :nimble_csv, "1.1.0", "b1dba4a86be9e03065c9de829050468e591f569100332db949e7ce71be0afc25", [:mix], [], "hexpm", "e986755bc302832cac429be6deda0fc9d82d3c82b47abefb68b3c17c9d949a3f"}, + "nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"}, + "nimble_ownership": {:hex, :nimble_ownership, "0.3.2", "d4fa4056ade0ae33b5a9eb64554a1b3779689282e37513260125d2d6b32e4874", [:mix], [], "hexpm", "28b9a9f4094fda1aa8ca72f732ff3223eb54aa3eda4fed9022254de2c152b138"}, + "nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"}, "nimble_totp": {:hex, :nimble_totp, "0.1.3", "fb7db78c672b4a96dc4386d46809d66d4a6ee2b3d2e69f22f38b394676c1ed6c", [:mix], [], "hexpm", "1263ac5bcef8e73ba2729d09cf125906d4f87c6dad602730e854740ef65d6040"}, "number": {:hex, :number, "1.0.3", "932c8a2d478a181c624138958ca88a78070332191b8061717270d939778c9857", [:mix], [{:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "dd397bbc096b2ca965a6a430126cc9cf7b9ef7421130def69bcf572232ca0f18"}, "parse_trans": {:hex, :parse_trans, "3.3.1", "16328ab840cc09919bd10dab29e431da3af9e9e7e7e6f0089dd5a2d2820011d8", [:rebar3], [], "hexpm", "07cd9577885f56362d414e8c4c4e6bdf10d43a8767abb92d24cbe8b24c54888b"}, @@ -54,7 +61,10 @@ "plug_crypto": {:hex, :plug_crypto, "2.1.0", "f44309c2b06d249c27c8d3f65cfe08158ade08418cf540fd4f72d4d6863abb7b", [:mix], [], "hexpm", "131216a4b030b8f8ce0f26038bc4421ae60e4bb95c5cf5395e1421437824c4fa"}, "postgrex": {:hex, :postgrex, "0.19.0", "f7d50e50cb42e0a185f5b9a6095125a9ab7e4abccfbe2ab820ab9aa92b71dbab", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "dba2d2a0a8637defbf2307e8629cb2526388ba7348f67d04ec77a5d6a72ecfae"}, "ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"}, + "recase": {:hex, :recase, "0.7.0", "3f2f719f0886c7a3b7fe469058ec539cb7bbe0023604ae3bce920e186305e5ae", [:mix], [], "hexpm", "36f5756a9f552f4a94b54a695870e32f4e72d5fad9c25e61bc4a3151c08a4e0c"}, + "req": {:hex, :req, "0.4.14", "103de133a076a31044e5458e0f850d5681eef23dfabf3ea34af63212e3b902e2", [:mix], [{:aws_signature, "~> 0.3.2", [hex: :aws_signature, repo: "hexpm", optional: true]}, {:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:nimble_ownership, "~> 0.2.0 or ~> 0.3.0", [hex: :nimble_ownership, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "2ddd3d33f9ab714ced8d3c15fd03db40c14dbf129003c4a3eb80fac2cc0b1b08"}, "rustler_precompiled": {:hex, :rustler_precompiled, "0.7.2", "097f657e401f02e7bc1cab808cfc6abdc1f7b9dc5e5adee46bf2fd8fdcce9ecf", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:rustler, "~> 0.23", [hex: :rustler, repo: "hexpm", optional: true]}], "hexpm", "7663faaeadc9e93e605164dcf9e69168e35f2f8b7f2b9eb4e400d1a8e0fe2999"}, + "saxy": {:hex, :saxy, "1.5.0", "0141127f2d042856f135fb2d94e0beecda7a2306f47546dbc6411fc5b07e28bf", [:mix], [], "hexpm", "ea7bb6328fbd1f2aceffa3ec6090bfb18c85aadf0f8e5030905e84235861cf89"}, "sobelow": {:hex, :sobelow, "0.11.1", "23438964486f8112b41e743bbfd402da3e5b296fdc9eacab29914b79c48916dd", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "9897363a7eff96f4809304a90aad819e2ad5e5d24db547af502885146746a53c"}, "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"}, "table": {:hex, :table, "0.1.2", "87ad1125f5b70c5dea0307aa633194083eb5182ec537efc94e96af08937e14a8", [:mix], [], "hexpm", "7e99bc7efef806315c7e65640724bf165c3061cdc5d854060f74468367065029"}, diff --git a/sample_data/bulk_fhir_download/Observation.ndjson b/sample_data/bulk_fhir_download/Observation.ndjson new file mode 100644 index 00000000..972027c8 --- /dev/null +++ b/sample_data/bulk_fhir_download/Observation.ndjson @@ -0,0 +1,2 @@ +{"resourceType":"Observation","id":"alice-result-1","meta":{"profile":["http://hl7.org/fhir/us/core/StructureDefinition/us-core-observation-lab"]},"extension":[{"url":"http://hl7.org/fhir/StructureDefinition/datereportedtolhd","valueDate":"08/05/2020"}],"category":[{"coding":[{"system":"http://terminology.hl7.org/CodeSystem/observation-category","code":"laboratory","display":"Laboratory"}]}],"status":"final","code":{"text":"TestTest"},"subject":{"reference":"Patient/10000"},"effectiveDateTime":"08/01/2020","issued":"2020-08-03T00:00:00Z","performer":[{"reference":"Organization/city-hospital-lab"}],"valueCodeableConcept":{"coding":[{"system":"http://snomed.info/sct","code":"10828004","display":"Positive"}]},"interpretation":[{"coding":[{"system":"http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation","code":"POS","display":"Positive"}]}]} +{"resourceType":"Observation","id":"billy-result-1","meta":{"profile":["http://hl7.org/fhir/us/core/StructureDefinition/us-core-observation-lab"]},"extension":[{"url":"http://hl7.org/fhir/StructureDefinition/datereportedtolhd","valueDate":"08/05/2020"}],"category":[{"coding":[{"system":"http://terminology.hl7.org/CodeSystem/observation-category","code":"laboratory","display":"Laboratory"}]}],"status":"final","code":{"text":"TestTest"},"subject":{"reference":"Patient/10004"},"effectiveDateTime":"08/02/2020","issued":"2020-08-04T00:00:00Z","performer":[{"reference":"Organization/city-hospital-lab"}],"valueCodeableConcept":{"coding":[{"system":"http://snomed.info/sct","code":"260385009","display":"Negative"}]},"interpretation":[{"coding":[{"system":"http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation","code":"NEG","display":"Negative"}]}]} \ No newline at end of file diff --git a/sample_data/bulk_fhir_download/Organization.ndjson b/sample_data/bulk_fhir_download/Organization.ndjson new file mode 100644 index 00000000..d29695df --- /dev/null +++ b/sample_data/bulk_fhir_download/Organization.ndjson @@ -0,0 +1,2 @@ +{"resourceType":"Organization","id":"city-hospital-lab","meta":{"profile":["http://hl7.org/fhir/us/core/StructureDefinition/us-core-organization|7.0.0"]},"active":true,"name":"City Hospital Lab"} +{"resourceType":"Organization","id":"lab-co-south","meta":{"profile":["http://hl7.org/fhir/us/core/StructureDefinition/us-core-organization|7.0.0"]},"active":true,"name":"Lab Co South"} \ No newline at end of file diff --git a/sample_data/bulk_fhir_download/Patient.ndjson b/sample_data/bulk_fhir_download/Patient.ndjson new file mode 100644 index 00000000..04b6d91f --- /dev/null +++ b/sample_data/bulk_fhir_download/Patient.ndjson @@ -0,0 +1,2 @@ +{"resourceType":"Patient","id":"10000","meta":{"profile":["http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient"]},"identifier":[{"system":"urn:example:person_tid","value":"alice"}],"name":[{"family":"Testuser","given":["Alice"]}],"birthDate":"1970-01-01","telecom":[{"system":"phone","value":"1111111000"}],"address":[{"line":["1234 Test St"],"city":"City","state":"TS","postalCode":"00000"}],"gender":"female","extension":[{"url":"http://hl7.org/fhir/us/core/StructureDefinition/us-core-race","extension":[{"url":"ombCategory","valueCoding":{"system":"urn:oid:2.16.840.1.113883.6.238","code":"2106-3","display":"White"}},{"url":"text","valueString":"White"}]},{"url":"http://hl7.org/fhir/us/core/StructureDefinition/us-core-ethnicity","extension":[{"url":"ombCategory","valueCoding":{"system":"urn:oid:2.16.840.1.113883.6.238","code":"2186-5","display":"Not Hispanic or Latino"}},{"url":"text","valueString":"Not Hispanic or Latino"}]},{"url":"http://hl7.org/fhir/StructureDefinition/patient-occupation","valueString":"Doctor"}]} +{"resourceType":"Patient","id":"10004","meta":{"profile":["http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient"]},"identifier":[{"system":"urn:example:person_tid","value":"billy"}],"name":[{"family":"Testuser","given":["Billy"]}],"birthDate":"1971-01-01","telecom":[{"system":"phone","value":"1111111004"}],"address":[{"line":["1234 Test St"],"city":"City","state":"TS","postalCode":"00000"}],"gender":"female","extension":[{"url":"http://hl7.org/fhir/us/core/StructureDefinition/us-core-race","extension":[{"url":"ombCategory","valueCoding":{"system":"urn:oid:2.16.840.1.113883.6.238","code":"2106-3","display":"White"}},{"url":"text","valueString":"White"}]},{"url":"http://hl7.org/fhir/us/core/StructureDefinition/us-core-ethnicity","extension":[{"url":"ombCategory","valueCoding":{"system":"urn:oid:2.16.840.1.113883.6.238","code":"2186-5","display":"Not Hispanic or Latino"}},{"url":"text","valueString":"Not Hispanic or Latino"}]},{"url":"http://hl7.org/fhir/StructureDefinition/patient-occupation","valueString":"Doctor"}]} \ No newline at end of file From 74f15535bbd2123348baf47c7ac6d6bf758fb330 Mon Sep 17 00:00:00 2001 From: Lei Zhou Date: Fri, 6 Sep 2024 16:43:06 -0400 Subject: [PATCH 2/8] add tests; all tests passed --- lib/epiviewpoint/bulk_fhir_parser.ex | 39 +++--- lib/epiviewpoint/cases.ex | 5 +- lib/epiviewpoint/cases/import.ex | 3 +- lib/epiviewpoint/data_file.ex | 4 +- lib/epiviewpoint/r4/address.ex | 10 +- lib/epiviewpoint/r4/age.ex | 8 +- lib/epiviewpoint/r4/annotation.ex | 10 +- lib/epiviewpoint/r4/attachment.ex | 8 +- lib/epiviewpoint/r4/codeable_concept.ex | 10 +- lib/epiviewpoint/r4/coding.ex | 8 +- lib/epiviewpoint/r4/contact_detail.ex | 10 +- lib/epiviewpoint/r4/contact_point.ex | 10 +- lib/epiviewpoint/r4/contributor.ex | 10 +- lib/epiviewpoint/r4/count.ex | 8 +- lib/epiviewpoint/r4/data_requirement.ex | 18 +-- .../r4/data_requirement/code_filter.ex | 12 +- .../r4/data_requirement/date_filter.ex | 14 +- lib/epiviewpoint/r4/data_requirement/sort.ex | 10 +- lib/epiviewpoint/r4/distance.ex | 8 +- lib/epiviewpoint/r4/dosage.ex | 30 ++-- lib/epiviewpoint/r4/dosage/dose_and_rate.ex | 22 +-- lib/epiviewpoint/r4/duration.ex | 8 +- lib/epiviewpoint/r4/element.ex | 8 +- lib/epiviewpoint/r4/expression.ex | 8 +- lib/epiviewpoint/r4/extension.ex | 70 +++++----- lib/epiviewpoint/r4/human_name.ex | 10 +- lib/epiviewpoint/r4/identifier.ex | 14 +- lib/epiviewpoint/r4/meta.ex | 12 +- lib/epiviewpoint/r4/money.ex | 8 +- lib/epiviewpoint/r4/narrative.ex | 8 +- lib/epiviewpoint/r4/observation.ex | 72 +++++----- lib/epiviewpoint/r4/observation/component.ex | 30 ++-- .../r4/observation/reference_range.ex | 20 +-- lib/epiviewpoint/r4/organization.ex | 30 ++-- lib/epiviewpoint/r4/organization/contact.ex | 18 +-- lib/epiviewpoint/r4/parameter_definition.ex | 8 +- lib/epiviewpoint/r4/patient.ex | 38 +++--- lib/epiviewpoint/r4/patient/communication.ex | 12 +- lib/epiviewpoint/r4/patient/contact.ex | 22 +-- lib/epiviewpoint/r4/patient/link.ex | 12 +- lib/epiviewpoint/r4/period.ex | 8 +- lib/epiviewpoint/r4/quantity.ex | 8 +- lib/epiviewpoint/r4/range.ex | 12 +- lib/epiviewpoint/r4/ratio.ex | 12 +- lib/epiviewpoint/r4/reference.ex | 10 +- lib/epiviewpoint/r4/related_artifact.ex | 10 +- lib/epiviewpoint/r4/resource_list.ex | 6 +- lib/epiviewpoint/r4/sampled_data.ex | 10 +- lib/epiviewpoint/r4/signature.ex | 14 +- lib/epiviewpoint/r4/timing.ex | 14 +- lib/epiviewpoint/r4/timing/repeat.ex | 16 +-- lib/epiviewpoint/r4/trigger_definition.ex | 16 +-- lib/epiviewpoint/r4/usage_context.ex | 18 +-- .../controllers/import_controller.ex | 3 - .../live/import_live.html.heex | 2 +- test/epiviewpoint/data_file_test.exs | 129 ++++++++++++++++++ .../controllers/import_controller_test.exs | 40 ++++++ 57 files changed, 582 insertions(+), 411 deletions(-) diff --git a/lib/epiviewpoint/bulk_fhir_parser.ex b/lib/epiviewpoint/bulk_fhir_parser.ex index 1f4dac32..89caeb1e 100644 --- a/lib/epiviewpoint/bulk_fhir_parser.ex +++ b/lib/epiviewpoint/bulk_fhir_parser.ex @@ -1,10 +1,12 @@ defmodule EpiViewpoint.BulkFhirParser do def parse_bulk_fhir(file_list) do + contents = file_list |> Enum.map(& &1.contents) |> List.to_string() + file_list |> load_resources() |> extract_resources() |> join_resources() - |> to_map() + |> to_map(contents) end def load_resources(file_list) do @@ -23,6 +25,7 @@ defmodule EpiViewpoint.BulkFhirParser do file_content |> String.split("\n") |> Stream.map(&String.trim/1) + |> Stream.filter(&(&1 != "")) |> Stream.map(&json_to_kindle_schema(&1, "EpiViewpoint.R4")) |> Enum.to_list() end @@ -37,21 +40,21 @@ defmodule EpiViewpoint.BulkFhirParser do def extract_resource("Patient", resource_list) do resource_list |> Enum.map(fn - %Epiviewpoint.R4.Patient{ + %EpiViewpoint.R4.Patient{ id: caseid, - identifier: [%Epiviewpoint.R4.Identifier{value: person_tid}], - name: [%Epiviewpoint.R4.HumanName{given: [search_firstname], family: search_lastname}], + identifier: [%EpiViewpoint.R4.Identifier{value: person_tid}], + name: [%EpiViewpoint.R4.HumanName{given: [search_firstname], family: search_lastname}], birth_date: dateofbirth, gender: sex, address: [ - %Epiviewpoint.R4.Address{ + %EpiViewpoint.R4.Address{ line: [diagaddress_street1], city: diagaddress_city, state: diagaddress_state, postal_code: diagaddress_zip } ], - telecom: [%Epiviewpoint.R4.ContactPoint{value: phonenumber}], + telecom: [%EpiViewpoint.R4.ContactPoint{value: phonenumber}], extension: extensions } = _resource -> %{ @@ -76,14 +79,14 @@ defmodule EpiViewpoint.BulkFhirParser do def extract_resource("Observation", resource_list) do resource_list |> Enum.map(fn - %Epiviewpoint.R4.Observation{ + %EpiViewpoint.R4.Observation{ id: lab_result_tid, - subject: %Epiviewpoint.R4.Reference{reference: "Patient/" <> pat_id}, + subject: %EpiViewpoint.R4.Reference{reference: "Patient/" <> pat_id}, effective_date_time: datecollected, issued: resultdate, - code: %Epiviewpoint.R4.CodeableConcept{text: testname}, - interpretation: [%Epiviewpoint.R4.CodeableConcept{coding: [%Epiviewpoint.R4.Coding{display: result}]}], - performer: [%Epiviewpoint.R4.Reference{reference: "Organization/" <> org_id}], + code: %EpiViewpoint.R4.CodeableConcept{text: testname}, + interpretation: [%EpiViewpoint.R4.CodeableConcept{coding: [%EpiViewpoint.R4.Coding{display: result}]}], + performer: [%EpiViewpoint.R4.Reference{reference: "Organization/" <> org_id}], extension: extensions } = _resource -> %{ @@ -102,7 +105,7 @@ defmodule EpiViewpoint.BulkFhirParser do def extract_resource("Organization", resource_list) do resource_list |> Enum.map(fn - %Epiviewpoint.R4.Organization{ + %EpiViewpoint.R4.Organization{ id: orgnization_id, name: orderingfacilityname } = _resource -> @@ -116,16 +119,16 @@ defmodule EpiViewpoint.BulkFhirParser do # Helper function to find extension values defp find_extension(extensions, url \\ nil) do Enum.find_value(extensions, fn - %Epiviewpoint.R4.Extension{ + %EpiViewpoint.R4.Extension{ url: ^url, - extension: [%Epiviewpoint.R4.Extension{url: "ombCategory", value_coding: %Epiviewpoint.R4.Coding{display: value}}, _] + extension: [%EpiViewpoint.R4.Extension{url: "ombCategory", value_coding: %EpiViewpoint.R4.Coding{display: value}}, _] } -> value - %Epiviewpoint.R4.Extension{url: "http://hl7.org/fhir/StructureDefinition/patient-occupation", value_string: value} -> + %EpiViewpoint.R4.Extension{url: "http://hl7.org/fhir/StructureDefinition/patient-occupation", value_string: value} -> value - %Epiviewpoint.R4.Extension{url: "http://hl7.org/fhir/StructureDefinition/datereportedtolhd", value_date: value} -> + %EpiViewpoint.R4.Extension{url: "http://hl7.org/fhir/StructureDefinition/datereportedtolhd", value_date: value} -> value _ -> @@ -155,7 +158,7 @@ defmodule EpiViewpoint.BulkFhirParser do end) end - def to_map(resources) do - %{file_name: "load.bulk_fhir", contents: resources} + def to_map(resources, contents) do + %{file_name: "load.bulk_fhir", contents: contents, list: resources} end end diff --git a/lib/epiviewpoint/cases.ex b/lib/epiviewpoint/cases.ex index 7929aff9..f656664d 100644 --- a/lib/epiviewpoint/cases.ex +++ b/lib/epiviewpoint/cases.ex @@ -34,7 +34,10 @@ defmodule EpiViewpoint.Cases do def count_lab_results(), do: LabResult |> Repo.aggregate(:count) def create_lab_result!({attrs, audit_meta}), do: %LabResult{} |> change_lab_result(attrs) |> AuditingRepo.insert!(audit_meta) def import_lab_results(lab_result_data_file_string, originator), do: Import.import_data_file(lab_result_data_file_string, originator) - def import_bulk_fhir_lab_results(lab_result_data_file_list, originator), do: Import.import_bulk_fhir_data_file(lab_result_data_file_list, originator) + + def import_bulk_fhir_lab_results(lab_result_data_file_list, originator), + do: Import.import_bulk_fhir_data_file(lab_result_data_file_list, originator) + def list_lab_results(), do: LabResult.Query.all() |> Repo.all() def preload_initiating_lab_result(case_investigations_or_nil), do: case_investigations_or_nil |> Repo.preload(:initiating_lab_result) def preload_lab_results(person_or_people_or_nil), do: person_or_people_or_nil |> Repo.preload(lab_results: LabResult.Query.display_order()) diff --git a/lib/epiviewpoint/cases/import.ex b/lib/epiviewpoint/cases/import.ex index 0a89c42b..7f667744 100644 --- a/lib/epiviewpoint/cases/import.ex +++ b/lib/epiviewpoint/cases/import.ex @@ -66,7 +66,6 @@ defmodule EpiViewpoint.Cases.Import do |> import_data_file(originator) end - def import_data_file(_file, %{admin: false}), do: {:error, "Originator must be an admin"} def import_data_file(file, %Accounts.User{} = originator) do @@ -111,7 +110,7 @@ defmodule EpiViewpoint.Cases.Import do case Path.extname(file.file_name) do ".csv" -> DataFile.read(file.contents, :csv, &rename_headers/1, @fields) ".ndjson" -> DataFile.read(file.contents, :ndjson, &rename_headers/1, @fields) - ".bulk_fhir" -> DataFile.read(file.contents, :bulk_fhir, &rename_headers/1, @fields) + ".bulk_fhir" -> DataFile.read(file.list, :bulk_fhir, &rename_headers/1, @fields) _ -> {:error, "Unsupported file type: #{file.extension}"} end end diff --git a/lib/epiviewpoint/data_file.ex b/lib/epiviewpoint/data_file.ex index d34a2bf7..d4015242 100644 --- a/lib/epiviewpoint/data_file.ex +++ b/lib/epiviewpoint/data_file.ex @@ -21,7 +21,7 @@ defmodule EpiViewpoint.DataFile do read(string, header_transformer, headers, &parse_ndjson/1) end - def read(string, :bulk_fhir, header_transformer, headers) when is_binary(string) do + def read(string, :bulk_fhir, header_transformer, headers) when is_list(string) do read(string, header_transformer, headers, &parse_bulk_fhir/1) end @@ -89,7 +89,7 @@ defmodule EpiViewpoint.DataFile do end defp parse_bulk_fhir(input) do - DataFrame.new(input) + {:ok, DataFrame.new(input)} end defp validate_csv(input) do diff --git a/lib/epiviewpoint/r4/address.ex b/lib/epiviewpoint/r4/address.ex index 028b97cd..d74338b9 100644 --- a/lib/epiviewpoint/r4/address.ex +++ b/lib/epiviewpoint/r4/address.ex @@ -1,4 +1,4 @@ -defmodule Epiviewpoint.R4.Address do +defmodule EpiViewpoint.R4.Address do use Ecto.Schema import Ecto.Changeset @@ -47,15 +47,15 @@ defmodule Epiviewpoint.R4.Address do ) # Embed One - embeds_one(:period, Epiviewpoint.R4.Period) + embeds_one(:period, EpiViewpoint.R4.Period) # Embed Many - embeds_many(:extension, Epiviewpoint.R4.Extension) + embeds_many(:extension, EpiViewpoint.R4.Extension) end def choices(_), do: nil - def version_namespace, do: Epiviewpoint.R4 + def version_namespace, do: EpiViewpoint.R4 def version, do: "R4" def changeset(data \\ %__MODULE__{}, attrs) do @@ -65,4 +65,4 @@ defmodule Epiviewpoint.R4.Address do |> cast_embed(:extension) |> validate_required(@required_fields) end -end \ No newline at end of file +end diff --git a/lib/epiviewpoint/r4/age.ex b/lib/epiviewpoint/r4/age.ex index df1305c5..8ba38614 100644 --- a/lib/epiviewpoint/r4/age.ex +++ b/lib/epiviewpoint/r4/age.ex @@ -1,4 +1,4 @@ -defmodule Epiviewpoint.R4.Age do +defmodule EpiViewpoint.R4.Age do use Ecto.Schema import Ecto.Changeset @@ -30,12 +30,12 @@ defmodule Epiviewpoint.R4.Age do ) # Embed Many - embeds_many(:extension, Epiviewpoint.R4.Extension) + embeds_many(:extension, EpiViewpoint.R4.Extension) end def choices(_), do: nil - def version_namespace, do: Epiviewpoint.R4 + def version_namespace, do: EpiViewpoint.R4 def version, do: "R4" def changeset(data \\ %__MODULE__{}, attrs) do @@ -44,4 +44,4 @@ defmodule Epiviewpoint.R4.Age do |> cast_embed(:extension) |> validate_required(@required_fields) end -end \ No newline at end of file +end diff --git a/lib/epiviewpoint/r4/annotation.ex b/lib/epiviewpoint/r4/annotation.ex index bd103fc7..af33ac59 100644 --- a/lib/epiviewpoint/r4/annotation.ex +++ b/lib/epiviewpoint/r4/annotation.ex @@ -1,4 +1,4 @@ -defmodule Epiviewpoint.R4.Annotation do +defmodule EpiViewpoint.R4.Annotation do use Ecto.Schema import Ecto.Changeset @@ -17,10 +17,10 @@ defmodule Epiviewpoint.R4.Annotation do field(:time, :utc_datetime_usec) # Embed One - embeds_one(:author_reference, Epiviewpoint.R4.Reference) + embeds_one(:author_reference, EpiViewpoint.R4.Reference) # Embed Many - embeds_many(:extension, Epiviewpoint.R4.Extension) + embeds_many(:extension, EpiViewpoint.R4.Extension) end def choices("author") do @@ -33,7 +33,7 @@ defmodule Epiviewpoint.R4.Annotation do def choices(_), do: nil - def version_namespace, do: Epiviewpoint.R4 + def version_namespace, do: EpiViewpoint.R4 def version, do: "R4" def changeset(data \\ %__MODULE__{}, attrs) do @@ -43,4 +43,4 @@ defmodule Epiviewpoint.R4.Annotation do |> cast_embed(:extension) |> validate_required(@required_fields) end -end \ No newline at end of file +end diff --git a/lib/epiviewpoint/r4/attachment.ex b/lib/epiviewpoint/r4/attachment.ex index 6d3121b1..2ec77fb3 100644 --- a/lib/epiviewpoint/r4/attachment.ex +++ b/lib/epiviewpoint/r4/attachment.ex @@ -1,4 +1,4 @@ -defmodule Epiviewpoint.R4.Attachment do +defmodule EpiViewpoint.R4.Attachment do use Ecto.Schema import Ecto.Changeset @@ -27,12 +27,12 @@ defmodule Epiviewpoint.R4.Attachment do field(:url, :string) # Embed Many - embeds_many(:extension, Epiviewpoint.R4.Extension) + embeds_many(:extension, EpiViewpoint.R4.Extension) end def choices(_), do: nil - def version_namespace, do: Epiviewpoint.R4 + def version_namespace, do: EpiViewpoint.R4 def version, do: "R4" def changeset(data \\ %__MODULE__{}, attrs) do @@ -41,4 +41,4 @@ defmodule Epiviewpoint.R4.Attachment do |> cast_embed(:extension) |> validate_required(@required_fields) end -end \ No newline at end of file +end diff --git a/lib/epiviewpoint/r4/codeable_concept.ex b/lib/epiviewpoint/r4/codeable_concept.ex index a7924e17..720022ef 100644 --- a/lib/epiviewpoint/r4/codeable_concept.ex +++ b/lib/epiviewpoint/r4/codeable_concept.ex @@ -1,4 +1,4 @@ -defmodule Epiviewpoint.R4.CodeableConcept do +defmodule EpiViewpoint.R4.CodeableConcept do use Ecto.Schema import Ecto.Changeset @@ -13,13 +13,13 @@ defmodule Epiviewpoint.R4.CodeableConcept do field(:text, :string) # Embed Many - embeds_many(:coding, Epiviewpoint.R4.Coding) - embeds_many(:extension, Epiviewpoint.R4.Extension) + embeds_many(:coding, EpiViewpoint.R4.Coding) + embeds_many(:extension, EpiViewpoint.R4.Extension) end def choices(_), do: nil - def version_namespace, do: Epiviewpoint.R4 + def version_namespace, do: EpiViewpoint.R4 def version, do: "R4" def changeset(data \\ %__MODULE__{}, attrs) do @@ -29,4 +29,4 @@ defmodule Epiviewpoint.R4.CodeableConcept do |> cast_embed(:extension) |> validate_required(@required_fields) end -end \ No newline at end of file +end diff --git a/lib/epiviewpoint/r4/coding.ex b/lib/epiviewpoint/r4/coding.ex index 8f9edad0..c4818428 100644 --- a/lib/epiviewpoint/r4/coding.ex +++ b/lib/epiviewpoint/r4/coding.ex @@ -1,4 +1,4 @@ -defmodule Epiviewpoint.R4.Coding do +defmodule EpiViewpoint.R4.Coding do use Ecto.Schema import Ecto.Changeset @@ -21,12 +21,12 @@ defmodule Epiviewpoint.R4.Coding do field(:version, :string) # Embed Many - embeds_many(:extension, Epiviewpoint.R4.Extension) + embeds_many(:extension, EpiViewpoint.R4.Extension) end def choices(_), do: nil - def version_namespace, do: Epiviewpoint.R4 + def version_namespace, do: EpiViewpoint.R4 def version, do: "R4" def changeset(data \\ %__MODULE__{}, attrs) do @@ -35,4 +35,4 @@ defmodule Epiviewpoint.R4.Coding do |> cast_embed(:extension) |> validate_required(@required_fields) end -end \ No newline at end of file +end diff --git a/lib/epiviewpoint/r4/contact_detail.ex b/lib/epiviewpoint/r4/contact_detail.ex index 1439daca..a02981ea 100644 --- a/lib/epiviewpoint/r4/contact_detail.ex +++ b/lib/epiviewpoint/r4/contact_detail.ex @@ -1,4 +1,4 @@ -defmodule Epiviewpoint.R4.ContactDetail do +defmodule EpiViewpoint.R4.ContactDetail do use Ecto.Schema import Ecto.Changeset @@ -13,13 +13,13 @@ defmodule Epiviewpoint.R4.ContactDetail do field(:name, :string) # Embed Many - embeds_many(:extension, Epiviewpoint.R4.Extension) - embeds_many(:telecom, Epiviewpoint.R4.ContactPoint) + embeds_many(:extension, EpiViewpoint.R4.Extension) + embeds_many(:telecom, EpiViewpoint.R4.ContactPoint) end def choices(_), do: nil - def version_namespace, do: Epiviewpoint.R4 + def version_namespace, do: EpiViewpoint.R4 def version, do: "R4" def changeset(data \\ %__MODULE__{}, attrs) do @@ -29,4 +29,4 @@ defmodule Epiviewpoint.R4.ContactDetail do |> cast_embed(:telecom) |> validate_required(@required_fields) end -end \ No newline at end of file +end diff --git a/lib/epiviewpoint/r4/contact_point.ex b/lib/epiviewpoint/r4/contact_point.ex index a23e86ab..b62d4840 100644 --- a/lib/epiviewpoint/r4/contact_point.ex +++ b/lib/epiviewpoint/r4/contact_point.ex @@ -1,4 +1,4 @@ -defmodule Epiviewpoint.R4.ContactPoint do +defmodule EpiViewpoint.R4.ContactPoint do use Ecto.Schema import Ecto.Changeset @@ -40,15 +40,15 @@ defmodule Epiviewpoint.R4.ContactPoint do ) # Embed One - embeds_one(:period, Epiviewpoint.R4.Period) + embeds_one(:period, EpiViewpoint.R4.Period) # Embed Many - embeds_many(:extension, Epiviewpoint.R4.Extension) + embeds_many(:extension, EpiViewpoint.R4.Extension) end def choices(_), do: nil - def version_namespace, do: Epiviewpoint.R4 + def version_namespace, do: EpiViewpoint.R4 def version, do: "R4" def changeset(data \\ %__MODULE__{}, attrs) do @@ -58,4 +58,4 @@ defmodule Epiviewpoint.R4.ContactPoint do |> cast_embed(:extension) |> validate_required(@required_fields) end -end \ No newline at end of file +end diff --git a/lib/epiviewpoint/r4/contributor.ex b/lib/epiviewpoint/r4/contributor.ex index c7241ca0..29165086 100644 --- a/lib/epiviewpoint/r4/contributor.ex +++ b/lib/epiviewpoint/r4/contributor.ex @@ -1,4 +1,4 @@ -defmodule Epiviewpoint.R4.Contributor do +defmodule EpiViewpoint.R4.Contributor do use Ecto.Schema import Ecto.Changeset @@ -24,13 +24,13 @@ defmodule Epiviewpoint.R4.Contributor do ) # Embed Many - embeds_many(:contact, Epiviewpoint.R4.ContactDetail) - embeds_many(:extension, Epiviewpoint.R4.Extension) + embeds_many(:contact, EpiViewpoint.R4.ContactDetail) + embeds_many(:extension, EpiViewpoint.R4.Extension) end def choices(_), do: nil - def version_namespace, do: Epiviewpoint.R4 + def version_namespace, do: EpiViewpoint.R4 def version, do: "R4" def changeset(data \\ %__MODULE__{}, attrs) do @@ -40,4 +40,4 @@ defmodule Epiviewpoint.R4.Contributor do |> cast_embed(:extension) |> validate_required(@required_fields) end -end \ No newline at end of file +end diff --git a/lib/epiviewpoint/r4/count.ex b/lib/epiviewpoint/r4/count.ex index 89bfe065..d087f028 100644 --- a/lib/epiviewpoint/r4/count.ex +++ b/lib/epiviewpoint/r4/count.ex @@ -1,4 +1,4 @@ -defmodule Epiviewpoint.R4.Count do +defmodule EpiViewpoint.R4.Count do use Ecto.Schema import Ecto.Changeset @@ -30,12 +30,12 @@ defmodule Epiviewpoint.R4.Count do ) # Embed Many - embeds_many(:extension, Epiviewpoint.R4.Extension) + embeds_many(:extension, EpiViewpoint.R4.Extension) end def choices(_), do: nil - def version_namespace, do: Epiviewpoint.R4 + def version_namespace, do: EpiViewpoint.R4 def version, do: "R4" def changeset(data \\ %__MODULE__{}, attrs) do @@ -44,4 +44,4 @@ defmodule Epiviewpoint.R4.Count do |> cast_embed(:extension) |> validate_required(@required_fields) end -end \ No newline at end of file +end diff --git a/lib/epiviewpoint/r4/data_requirement.ex b/lib/epiviewpoint/r4/data_requirement.ex index 546433d9..24b4a9de 100644 --- a/lib/epiviewpoint/r4/data_requirement.ex +++ b/lib/epiviewpoint/r4/data_requirement.ex @@ -1,4 +1,4 @@ -defmodule Epiviewpoint.R4.DataRequirement do +defmodule EpiViewpoint.R4.DataRequirement do use Ecto.Schema import Ecto.Changeset @@ -20,14 +20,14 @@ defmodule Epiviewpoint.R4.DataRequirement do field(:profile, {:array, :string}) # Embed One - embeds_one(:subject_codeable_concept, Epiviewpoint.R4.CodeableConcept) - embeds_one(:subject_reference, Epiviewpoint.R4.Reference) + embeds_one(:subject_codeable_concept, EpiViewpoint.R4.CodeableConcept) + embeds_one(:subject_reference, EpiViewpoint.R4.Reference) # Embed Many - embeds_many(:code_filter, Epiviewpoint.R4.DataRequirement.CodeFilter) - embeds_many(:date_filter, Epiviewpoint.R4.DataRequirement.DateFilter) - embeds_many(:extension, Epiviewpoint.R4.Extension) - embeds_many(:sort, Epiviewpoint.R4.DataRequirement.Sort) + embeds_many(:code_filter, EpiViewpoint.R4.DataRequirement.CodeFilter) + embeds_many(:date_filter, EpiViewpoint.R4.DataRequirement.DateFilter) + embeds_many(:extension, EpiViewpoint.R4.Extension) + embeds_many(:sort, EpiViewpoint.R4.DataRequirement.Sort) end def choices("subject") do @@ -40,7 +40,7 @@ defmodule Epiviewpoint.R4.DataRequirement do def choices(_), do: nil - def version_namespace, do: Epiviewpoint.R4 + def version_namespace, do: EpiViewpoint.R4 def version, do: "R4" def changeset(data \\ %__MODULE__{}, attrs) do @@ -54,4 +54,4 @@ defmodule Epiviewpoint.R4.DataRequirement do |> cast_embed(:sort) |> validate_required(@required_fields) end -end \ No newline at end of file +end diff --git a/lib/epiviewpoint/r4/data_requirement/code_filter.ex b/lib/epiviewpoint/r4/data_requirement/code_filter.ex index 6c5d50ff..73c9530f 100644 --- a/lib/epiviewpoint/r4/data_requirement/code_filter.ex +++ b/lib/epiviewpoint/r4/data_requirement/code_filter.ex @@ -1,4 +1,4 @@ -defmodule Epiviewpoint.R4.DataRequirement.CodeFilter do +defmodule EpiViewpoint.R4.DataRequirement.CodeFilter do use Ecto.Schema import Ecto.Changeset @@ -17,14 +17,14 @@ defmodule Epiviewpoint.R4.DataRequirement.CodeFilter do field(:value_set, :string) # Embed Many - embeds_many(:code, Epiviewpoint.R4.Coding) - embeds_many(:extension, Epiviewpoint.R4.Extension) - embeds_many(:modifier_extension, Epiviewpoint.R4.Extension) + embeds_many(:code, EpiViewpoint.R4.Coding) + embeds_many(:extension, EpiViewpoint.R4.Extension) + embeds_many(:modifier_extension, EpiViewpoint.R4.Extension) end def choices(_), do: nil - def version_namespace, do: Epiviewpoint.R4 + def version_namespace, do: EpiViewpoint.R4 def version, do: "R4" def changeset(data \\ %__MODULE__{}, attrs) do @@ -35,4 +35,4 @@ defmodule Epiviewpoint.R4.DataRequirement.CodeFilter do |> cast_embed(:modifier_extension) |> validate_required(@required_fields) end -end \ No newline at end of file +end diff --git a/lib/epiviewpoint/r4/data_requirement/date_filter.ex b/lib/epiviewpoint/r4/data_requirement/date_filter.ex index 2b9dce0f..63684556 100644 --- a/lib/epiviewpoint/r4/data_requirement/date_filter.ex +++ b/lib/epiviewpoint/r4/data_requirement/date_filter.ex @@ -1,4 +1,4 @@ -defmodule Epiviewpoint.R4.DataRequirement.DateFilter do +defmodule EpiViewpoint.R4.DataRequirement.DateFilter do use Ecto.Schema import Ecto.Changeset @@ -17,12 +17,12 @@ defmodule Epiviewpoint.R4.DataRequirement.DateFilter do field(:value_date_time, :string) # Embed One - embeds_one(:value_duration, Epiviewpoint.R4.Duration) - embeds_one(:value_period, Epiviewpoint.R4.Period) + embeds_one(:value_duration, EpiViewpoint.R4.Duration) + embeds_one(:value_period, EpiViewpoint.R4.Period) # Embed Many - embeds_many(:extension, Epiviewpoint.R4.Extension) - embeds_many(:modifier_extension, Epiviewpoint.R4.Extension) + embeds_many(:extension, EpiViewpoint.R4.Extension) + embeds_many(:modifier_extension, EpiViewpoint.R4.Extension) end def choices("value") do @@ -37,7 +37,7 @@ defmodule Epiviewpoint.R4.DataRequirement.DateFilter do def choices(_), do: nil - def version_namespace, do: Epiviewpoint.R4 + def version_namespace, do: EpiViewpoint.R4 def version, do: "R4" def changeset(data \\ %__MODULE__{}, attrs) do @@ -49,4 +49,4 @@ defmodule Epiviewpoint.R4.DataRequirement.DateFilter do |> cast_embed(:modifier_extension) |> validate_required(@required_fields) end -end \ No newline at end of file +end diff --git a/lib/epiviewpoint/r4/data_requirement/sort.ex b/lib/epiviewpoint/r4/data_requirement/sort.ex index f465cf05..e99d7e19 100644 --- a/lib/epiviewpoint/r4/data_requirement/sort.ex +++ b/lib/epiviewpoint/r4/data_requirement/sort.ex @@ -1,4 +1,4 @@ -defmodule Epiviewpoint.R4.DataRequirement.Sort do +defmodule EpiViewpoint.R4.DataRequirement.Sort do use Ecto.Schema import Ecto.Changeset @@ -22,13 +22,13 @@ defmodule Epiviewpoint.R4.DataRequirement.Sort do ) # Embed Many - embeds_many(:extension, Epiviewpoint.R4.Extension) - embeds_many(:modifier_extension, Epiviewpoint.R4.Extension) + embeds_many(:extension, EpiViewpoint.R4.Extension) + embeds_many(:modifier_extension, EpiViewpoint.R4.Extension) end def choices(_), do: nil - def version_namespace, do: Epiviewpoint.R4 + def version_namespace, do: EpiViewpoint.R4 def version, do: "R4" def changeset(data \\ %__MODULE__{}, attrs) do @@ -38,4 +38,4 @@ defmodule Epiviewpoint.R4.DataRequirement.Sort do |> cast_embed(:modifier_extension) |> validate_required(@required_fields) end -end \ No newline at end of file +end diff --git a/lib/epiviewpoint/r4/distance.ex b/lib/epiviewpoint/r4/distance.ex index e70e5554..f4d0a7f5 100644 --- a/lib/epiviewpoint/r4/distance.ex +++ b/lib/epiviewpoint/r4/distance.ex @@ -1,4 +1,4 @@ -defmodule Epiviewpoint.R4.Distance do +defmodule EpiViewpoint.R4.Distance do use Ecto.Schema import Ecto.Changeset @@ -30,12 +30,12 @@ defmodule Epiviewpoint.R4.Distance do ) # Embed Many - embeds_many(:extension, Epiviewpoint.R4.Extension) + embeds_many(:extension, EpiViewpoint.R4.Extension) end def choices(_), do: nil - def version_namespace, do: Epiviewpoint.R4 + def version_namespace, do: EpiViewpoint.R4 def version, do: "R4" def changeset(data \\ %__MODULE__{}, attrs) do @@ -44,4 +44,4 @@ defmodule Epiviewpoint.R4.Distance do |> cast_embed(:extension) |> validate_required(@required_fields) end -end \ No newline at end of file +end diff --git a/lib/epiviewpoint/r4/dosage.ex b/lib/epiviewpoint/r4/dosage.ex index 0b2e21e4..7bd05eac 100644 --- a/lib/epiviewpoint/r4/dosage.ex +++ b/lib/epiviewpoint/r4/dosage.ex @@ -1,4 +1,4 @@ -defmodule Epiviewpoint.R4.Dosage do +defmodule EpiViewpoint.R4.Dosage do use Ecto.Schema import Ecto.Changeset @@ -19,20 +19,20 @@ defmodule Epiviewpoint.R4.Dosage do field(:text, :string) # Embed One - embeds_one(:as_needed_codeable_concept, Epiviewpoint.R4.CodeableConcept) - embeds_one(:max_dose_per_administration, Epiviewpoint.R4.Quantity) - embeds_one(:max_dose_per_lifetime, Epiviewpoint.R4.Quantity) - embeds_one(:max_dose_per_period, Epiviewpoint.R4.Ratio) - embeds_one(:method, Epiviewpoint.R4.CodeableConcept) - embeds_one(:route, Epiviewpoint.R4.CodeableConcept) - embeds_one(:site, Epiviewpoint.R4.CodeableConcept) - embeds_one(:timing, Epiviewpoint.R4.Timing) + embeds_one(:as_needed_codeable_concept, EpiViewpoint.R4.CodeableConcept) + embeds_one(:max_dose_per_administration, EpiViewpoint.R4.Quantity) + embeds_one(:max_dose_per_lifetime, EpiViewpoint.R4.Quantity) + embeds_one(:max_dose_per_period, EpiViewpoint.R4.Ratio) + embeds_one(:method, EpiViewpoint.R4.CodeableConcept) + embeds_one(:route, EpiViewpoint.R4.CodeableConcept) + embeds_one(:site, EpiViewpoint.R4.CodeableConcept) + embeds_one(:timing, EpiViewpoint.R4.Timing) # Embed Many - embeds_many(:additional_instruction, Epiviewpoint.R4.CodeableConcept) - embeds_many(:dose_and_rate, Epiviewpoint.R4.Dosage.DoseAndRate) - embeds_many(:extension, Epiviewpoint.R4.Extension) - embeds_many(:modifier_extension, Epiviewpoint.R4.Extension) + embeds_many(:additional_instruction, EpiViewpoint.R4.CodeableConcept) + embeds_many(:dose_and_rate, EpiViewpoint.R4.Dosage.DoseAndRate) + embeds_many(:extension, EpiViewpoint.R4.Extension) + embeds_many(:modifier_extension, EpiViewpoint.R4.Extension) end def choices("asNeeded") do @@ -45,7 +45,7 @@ defmodule Epiviewpoint.R4.Dosage do def choices(_), do: nil - def version_namespace, do: Epiviewpoint.R4 + def version_namespace, do: EpiViewpoint.R4 def version, do: "R4" def changeset(data \\ %__MODULE__{}, attrs) do @@ -65,4 +65,4 @@ defmodule Epiviewpoint.R4.Dosage do |> cast_embed(:modifier_extension) |> validate_required(@required_fields) end -end \ No newline at end of file +end diff --git a/lib/epiviewpoint/r4/dosage/dose_and_rate.ex b/lib/epiviewpoint/r4/dosage/dose_and_rate.ex index 2fff6029..965eade0 100644 --- a/lib/epiviewpoint/r4/dosage/dose_and_rate.ex +++ b/lib/epiviewpoint/r4/dosage/dose_and_rate.ex @@ -1,4 +1,4 @@ -defmodule Epiviewpoint.R4.Dosage.DoseAndRate do +defmodule EpiViewpoint.R4.Dosage.DoseAndRate do use Ecto.Schema import Ecto.Changeset @@ -9,16 +9,16 @@ defmodule Epiviewpoint.R4.Dosage.DoseAndRate do embedded_schema do # Embed One - embeds_one(:dose_quantity, Epiviewpoint.R4.Quantity) - embeds_one(:dose_range, Epiviewpoint.R4.Range) - embeds_one(:rate_quantity, Epiviewpoint.R4.Quantity) - embeds_one(:rate_range, Epiviewpoint.R4.Range) - embeds_one(:rate_ratio, Epiviewpoint.R4.Ratio) - embeds_one(:type, Epiviewpoint.R4.CodeableConcept) + embeds_one(:dose_quantity, EpiViewpoint.R4.Quantity) + embeds_one(:dose_range, EpiViewpoint.R4.Range) + embeds_one(:rate_quantity, EpiViewpoint.R4.Quantity) + embeds_one(:rate_range, EpiViewpoint.R4.Range) + embeds_one(:rate_ratio, EpiViewpoint.R4.Ratio) + embeds_one(:type, EpiViewpoint.R4.CodeableConcept) # Embed Many - embeds_many(:extension, Epiviewpoint.R4.Extension) - embeds_many(:modifier_extension, Epiviewpoint.R4.Extension) + embeds_many(:extension, EpiViewpoint.R4.Extension) + embeds_many(:modifier_extension, EpiViewpoint.R4.Extension) end def choices("dose") do @@ -41,7 +41,7 @@ defmodule Epiviewpoint.R4.Dosage.DoseAndRate do def choices(_), do: nil - def version_namespace, do: Epiviewpoint.R4 + def version_namespace, do: EpiViewpoint.R4 def version, do: "R4" def changeset(data \\ %__MODULE__{}, attrs) do @@ -57,4 +57,4 @@ defmodule Epiviewpoint.R4.Dosage.DoseAndRate do |> cast_embed(:modifier_extension) |> validate_required(@required_fields) end -end \ No newline at end of file +end diff --git a/lib/epiviewpoint/r4/duration.ex b/lib/epiviewpoint/r4/duration.ex index 4c4cdb30..f0004082 100644 --- a/lib/epiviewpoint/r4/duration.ex +++ b/lib/epiviewpoint/r4/duration.ex @@ -1,4 +1,4 @@ -defmodule Epiviewpoint.R4.Duration do +defmodule EpiViewpoint.R4.Duration do use Ecto.Schema import Ecto.Changeset @@ -30,12 +30,12 @@ defmodule Epiviewpoint.R4.Duration do ) # Embed Many - embeds_many(:extension, Epiviewpoint.R4.Extension) + embeds_many(:extension, EpiViewpoint.R4.Extension) end def choices(_), do: nil - def version_namespace, do: Epiviewpoint.R4 + def version_namespace, do: EpiViewpoint.R4 def version, do: "R4" def changeset(data \\ %__MODULE__{}, attrs) do @@ -44,4 +44,4 @@ defmodule Epiviewpoint.R4.Duration do |> cast_embed(:extension) |> validate_required(@required_fields) end -end \ No newline at end of file +end diff --git a/lib/epiviewpoint/r4/element.ex b/lib/epiviewpoint/r4/element.ex index 5af02ad7..06fef920 100644 --- a/lib/epiviewpoint/r4/element.ex +++ b/lib/epiviewpoint/r4/element.ex @@ -1,4 +1,4 @@ -defmodule Epiviewpoint.R4.Element do +defmodule EpiViewpoint.R4.Element do use Ecto.Schema import Ecto.Changeset @@ -9,12 +9,12 @@ defmodule Epiviewpoint.R4.Element do embedded_schema do # Embed Many - embeds_many(:extension, Epiviewpoint.R4.Extension) + embeds_many(:extension, EpiViewpoint.R4.Extension) end def choices(_), do: nil - def version_namespace, do: Epiviewpoint.R4 + def version_namespace, do: EpiViewpoint.R4 def version, do: "R4" def changeset(data \\ %__MODULE__{}, attrs) do @@ -23,4 +23,4 @@ defmodule Epiviewpoint.R4.Element do |> cast_embed(:extension) |> validate_required(@required_fields) end -end \ No newline at end of file +end diff --git a/lib/epiviewpoint/r4/expression.ex b/lib/epiviewpoint/r4/expression.ex index 241c3e34..a62ea029 100644 --- a/lib/epiviewpoint/r4/expression.ex +++ b/lib/epiviewpoint/r4/expression.ex @@ -1,4 +1,4 @@ -defmodule Epiviewpoint.R4.Expression do +defmodule EpiViewpoint.R4.Expression do use Ecto.Schema import Ecto.Changeset @@ -29,12 +29,12 @@ defmodule Epiviewpoint.R4.Expression do ) # Embed Many - embeds_many(:extension, Epiviewpoint.R4.Extension) + embeds_many(:extension, EpiViewpoint.R4.Extension) end def choices(_), do: nil - def version_namespace, do: Epiviewpoint.R4 + def version_namespace, do: EpiViewpoint.R4 def version, do: "R4" def changeset(data \\ %__MODULE__{}, attrs) do @@ -43,4 +43,4 @@ defmodule Epiviewpoint.R4.Expression do |> cast_embed(:extension) |> validate_required(@required_fields) end -end \ No newline at end of file +end diff --git a/lib/epiviewpoint/r4/extension.ex b/lib/epiviewpoint/r4/extension.ex index c8ab3b50..c902bef8 100644 --- a/lib/epiviewpoint/r4/extension.ex +++ b/lib/epiviewpoint/r4/extension.ex @@ -1,4 +1,4 @@ -defmodule Epiviewpoint.R4.Extension do +defmodule EpiViewpoint.R4.Extension do use Ecto.Schema import Ecto.Changeset @@ -51,45 +51,45 @@ defmodule Epiviewpoint.R4.Extension do field(:value_id, :string) # Embed One - embeds_one(:value_quantity, Epiviewpoint.R4.Quantity) - embeds_one(:value_expression, Epiviewpoint.R4.Expression) - embeds_one(:value_attachment, Epiviewpoint.R4.Attachment) - embeds_one(:value_identifier, Epiviewpoint.R4.Identifier) - embeds_one(:value_sampled_data, Epiviewpoint.R4.SampledData) - embeds_one(:value_parameter_definition, Epiviewpoint.R4.ParameterDefinition) - embeds_one(:value_timing, Epiviewpoint.R4.Timing) - embeds_one(:value_reference, Epiviewpoint.R4.Reference) - embeds_one(:value_contact_point, Epiviewpoint.R4.ContactPoint) - embeds_one(:value_age, Epiviewpoint.R4.Age) - embeds_one(:value_meta, Epiviewpoint.R4.Meta) - embeds_one(:value_annotation, Epiviewpoint.R4.Annotation) - embeds_one(:value_money, Epiviewpoint.R4.Money) - embeds_one(:value_usage_context, Epiviewpoint.R4.UsageContext) - embeds_one(:value_related_artifact, Epiviewpoint.R4.RelatedArtifact) - embeds_one(:value_contact_detail, Epiviewpoint.R4.ContactDetail) - embeds_one(:value_ratio, Epiviewpoint.R4.Ratio) - embeds_one(:value_distance, Epiviewpoint.R4.Distance) - embeds_one(:value_duration, Epiviewpoint.R4.Duration) - embeds_one(:value_human_name, Epiviewpoint.R4.HumanName) - embeds_one(:value_period, Epiviewpoint.R4.Period) - embeds_one(:value_range, Epiviewpoint.R4.Range) - embeds_one(:value_dosage, Epiviewpoint.R4.Dosage) - embeds_one(:value_contributor, Epiviewpoint.R4.Contributor) - embeds_one(:value_address, Epiviewpoint.R4.Address) - embeds_one(:value_signature, Epiviewpoint.R4.Signature) - embeds_one(:value_trigger_definition, Epiviewpoint.R4.TriggerDefinition) - embeds_one(:value_data_requirement, Epiviewpoint.R4.DataRequirement) - embeds_one(:value_count, Epiviewpoint.R4.Count) - embeds_one(:value_coding, Epiviewpoint.R4.Coding) - embeds_one(:value_codeable_concept, Epiviewpoint.R4.CodeableConcept) + embeds_one(:value_quantity, EpiViewpoint.R4.Quantity) + embeds_one(:value_expression, EpiViewpoint.R4.Expression) + embeds_one(:value_attachment, EpiViewpoint.R4.Attachment) + embeds_one(:value_identifier, EpiViewpoint.R4.Identifier) + embeds_one(:value_sampled_data, EpiViewpoint.R4.SampledData) + embeds_one(:value_parameter_definition, EpiViewpoint.R4.ParameterDefinition) + embeds_one(:value_timing, EpiViewpoint.R4.Timing) + embeds_one(:value_reference, EpiViewpoint.R4.Reference) + embeds_one(:value_contact_point, EpiViewpoint.R4.ContactPoint) + embeds_one(:value_age, EpiViewpoint.R4.Age) + embeds_one(:value_meta, EpiViewpoint.R4.Meta) + embeds_one(:value_annotation, EpiViewpoint.R4.Annotation) + embeds_one(:value_money, EpiViewpoint.R4.Money) + embeds_one(:value_usage_context, EpiViewpoint.R4.UsageContext) + embeds_one(:value_related_artifact, EpiViewpoint.R4.RelatedArtifact) + embeds_one(:value_contact_detail, EpiViewpoint.R4.ContactDetail) + embeds_one(:value_ratio, EpiViewpoint.R4.Ratio) + embeds_one(:value_distance, EpiViewpoint.R4.Distance) + embeds_one(:value_duration, EpiViewpoint.R4.Duration) + embeds_one(:value_human_name, EpiViewpoint.R4.HumanName) + embeds_one(:value_period, EpiViewpoint.R4.Period) + embeds_one(:value_range, EpiViewpoint.R4.Range) + embeds_one(:value_dosage, EpiViewpoint.R4.Dosage) + embeds_one(:value_contributor, EpiViewpoint.R4.Contributor) + embeds_one(:value_address, EpiViewpoint.R4.Address) + embeds_one(:value_signature, EpiViewpoint.R4.Signature) + embeds_one(:value_trigger_definition, EpiViewpoint.R4.TriggerDefinition) + embeds_one(:value_data_requirement, EpiViewpoint.R4.DataRequirement) + embeds_one(:value_count, EpiViewpoint.R4.Count) + embeds_one(:value_coding, EpiViewpoint.R4.Coding) + embeds_one(:value_codeable_concept, EpiViewpoint.R4.CodeableConcept) # Embed Many - embeds_many(:extension, Epiviewpoint.R4.Extension) + embeds_many(:extension, EpiViewpoint.R4.Extension) end def choices(_), do: nil - def version_namespace, do: Epiviewpoint.R4 + def version_namespace, do: EpiViewpoint.R4 def version, do: "R4" def changeset(data \\ %__MODULE__{}, attrs) do @@ -129,4 +129,4 @@ defmodule Epiviewpoint.R4.Extension do |> cast_embed(:extension) |> validate_required(@required_fields) end -end \ No newline at end of file +end diff --git a/lib/epiviewpoint/r4/human_name.ex b/lib/epiviewpoint/r4/human_name.ex index 8ed99171..145b3340 100644 --- a/lib/epiviewpoint/r4/human_name.ex +++ b/lib/epiviewpoint/r4/human_name.ex @@ -1,4 +1,4 @@ -defmodule Epiviewpoint.R4.HumanName do +defmodule EpiViewpoint.R4.HumanName do use Ecto.Schema import Ecto.Changeset @@ -36,15 +36,15 @@ defmodule Epiviewpoint.R4.HumanName do ) # Embed One - embeds_one(:period, Epiviewpoint.R4.Period) + embeds_one(:period, EpiViewpoint.R4.Period) # Embed Many - embeds_many(:extension, Epiviewpoint.R4.Extension) + embeds_many(:extension, EpiViewpoint.R4.Extension) end def choices(_), do: nil - def version_namespace, do: Epiviewpoint.R4 + def version_namespace, do: EpiViewpoint.R4 def version, do: "R4" def changeset(data \\ %__MODULE__{}, attrs) do @@ -54,4 +54,4 @@ defmodule Epiviewpoint.R4.HumanName do |> cast_embed(:extension) |> validate_required(@required_fields) end -end \ No newline at end of file +end diff --git a/lib/epiviewpoint/r4/identifier.ex b/lib/epiviewpoint/r4/identifier.ex index d2471bc7..a4be948a 100644 --- a/lib/epiviewpoint/r4/identifier.ex +++ b/lib/epiviewpoint/r4/identifier.ex @@ -1,4 +1,4 @@ -defmodule Epiviewpoint.R4.Identifier do +defmodule EpiViewpoint.R4.Identifier do use Ecto.Schema import Ecto.Changeset @@ -27,17 +27,17 @@ defmodule Epiviewpoint.R4.Identifier do ) # Embed One - embeds_one(:assigner, Epiviewpoint.R4.Reference) - embeds_one(:period, Epiviewpoint.R4.Period) - embeds_one(:type, Epiviewpoint.R4.CodeableConcept) + embeds_one(:assigner, EpiViewpoint.R4.Reference) + embeds_one(:period, EpiViewpoint.R4.Period) + embeds_one(:type, EpiViewpoint.R4.CodeableConcept) # Embed Many - embeds_many(:extension, Epiviewpoint.R4.Extension) + embeds_many(:extension, EpiViewpoint.R4.Extension) end def choices(_), do: nil - def version_namespace, do: Epiviewpoint.R4 + def version_namespace, do: EpiViewpoint.R4 def version, do: "R4" def changeset(data \\ %__MODULE__{}, attrs) do @@ -49,4 +49,4 @@ defmodule Epiviewpoint.R4.Identifier do |> cast_embed(:extension) |> validate_required(@required_fields) end -end \ No newline at end of file +end diff --git a/lib/epiviewpoint/r4/meta.ex b/lib/epiviewpoint/r4/meta.ex index 8632b8b7..0fd539fb 100644 --- a/lib/epiviewpoint/r4/meta.ex +++ b/lib/epiviewpoint/r4/meta.ex @@ -1,4 +1,4 @@ -defmodule Epiviewpoint.R4.Meta do +defmodule EpiViewpoint.R4.Meta do use Ecto.Schema import Ecto.Changeset @@ -20,14 +20,14 @@ defmodule Epiviewpoint.R4.Meta do field(:profile, {:array, :string}) # Embed Many - embeds_many(:extension, Epiviewpoint.R4.Extension) - embeds_many(:security, Epiviewpoint.R4.Coding) - embeds_many(:tag, Epiviewpoint.R4.Coding) + embeds_many(:extension, EpiViewpoint.R4.Extension) + embeds_many(:security, EpiViewpoint.R4.Coding) + embeds_many(:tag, EpiViewpoint.R4.Coding) end def choices(_), do: nil - def version_namespace, do: Epiviewpoint.R4 + def version_namespace, do: EpiViewpoint.R4 def version, do: "R4" def changeset(data \\ %__MODULE__{}, attrs) do @@ -38,4 +38,4 @@ defmodule Epiviewpoint.R4.Meta do |> cast_embed(:tag) |> validate_required(@required_fields) end -end \ No newline at end of file +end diff --git a/lib/epiviewpoint/r4/money.ex b/lib/epiviewpoint/r4/money.ex index 6014c0af..0f9aa005 100644 --- a/lib/epiviewpoint/r4/money.ex +++ b/lib/epiviewpoint/r4/money.ex @@ -1,4 +1,4 @@ -defmodule Epiviewpoint.R4.Money do +defmodule EpiViewpoint.R4.Money do use Ecto.Schema import Ecto.Changeset @@ -15,12 +15,12 @@ defmodule Epiviewpoint.R4.Money do field(:value, :decimal) # Embed Many - embeds_many(:extension, Epiviewpoint.R4.Extension) + embeds_many(:extension, EpiViewpoint.R4.Extension) end def choices(_), do: nil - def version_namespace, do: Epiviewpoint.R4 + def version_namespace, do: EpiViewpoint.R4 def version, do: "R4" def changeset(data \\ %__MODULE__{}, attrs) do @@ -29,4 +29,4 @@ defmodule Epiviewpoint.R4.Money do |> cast_embed(:extension) |> validate_required(@required_fields) end -end \ No newline at end of file +end diff --git a/lib/epiviewpoint/r4/narrative.ex b/lib/epiviewpoint/r4/narrative.ex index 6798aa4d..4f59f03f 100644 --- a/lib/epiviewpoint/r4/narrative.ex +++ b/lib/epiviewpoint/r4/narrative.ex @@ -1,4 +1,4 @@ -defmodule Epiviewpoint.R4.Narrative do +defmodule EpiViewpoint.R4.Narrative do use Ecto.Schema import Ecto.Changeset @@ -26,12 +26,12 @@ defmodule Epiviewpoint.R4.Narrative do ) # Embed Many - embeds_many(:extension, Epiviewpoint.R4.Extension) + embeds_many(:extension, EpiViewpoint.R4.Extension) end def choices(_), do: nil - def version_namespace, do: Epiviewpoint.R4 + def version_namespace, do: EpiViewpoint.R4 def version, do: "R4" def changeset(data \\ %__MODULE__{}, attrs) do @@ -40,4 +40,4 @@ defmodule Epiviewpoint.R4.Narrative do |> cast_embed(:extension) |> validate_required(@required_fields) end -end \ No newline at end of file +end diff --git a/lib/epiviewpoint/r4/observation.ex b/lib/epiviewpoint/r4/observation.ex index 81449b4c..298c2703 100644 --- a/lib/epiviewpoint/r4/observation.ex +++ b/lib/epiviewpoint/r4/observation.ex @@ -1,4 +1,4 @@ -defmodule Epiviewpoint.R4.Observation do +defmodule EpiViewpoint.R4.Observation do use Ecto.Schema import Ecto.Changeset @@ -50,41 +50,41 @@ defmodule Epiviewpoint.R4.Observation do ) # Embed One - embeds_one(:value_quantity, Epiviewpoint.R4.Quantity) - embeds_one(:effective_timing, Epiviewpoint.R4.Timing) - embeds_one(:value_sampled_data, Epiviewpoint.R4.SampledData) - embeds_one(:specimen, Epiviewpoint.R4.Reference) - embeds_one(:effective_period, Epiviewpoint.R4.Period) - embeds_one(:value_ratio, Epiviewpoint.R4.Ratio) - embeds_one(:encounter, Epiviewpoint.R4.Reference) - embeds_one(:code, Epiviewpoint.R4.CodeableConcept) - embeds_one(:subject, Epiviewpoint.R4.Reference) - embeds_one(:data_absent_reason, Epiviewpoint.R4.CodeableConcept) - embeds_one(:text, Epiviewpoint.R4.Narrative) - embeds_one(:body_site, Epiviewpoint.R4.CodeableConcept) - embeds_one(:meta, Epiviewpoint.R4.Meta) - embeds_one(:value_period, Epiviewpoint.R4.Period) - embeds_one(:value_range, Epiviewpoint.R4.Range) - embeds_one(:method, Epiviewpoint.R4.CodeableConcept) - embeds_one(:device, Epiviewpoint.R4.Reference) - embeds_one(:value_codeable_concept, Epiviewpoint.R4.CodeableConcept) + embeds_one(:value_quantity, EpiViewpoint.R4.Quantity) + embeds_one(:effective_timing, EpiViewpoint.R4.Timing) + embeds_one(:value_sampled_data, EpiViewpoint.R4.SampledData) + embeds_one(:specimen, EpiViewpoint.R4.Reference) + embeds_one(:effective_period, EpiViewpoint.R4.Period) + embeds_one(:value_ratio, EpiViewpoint.R4.Ratio) + embeds_one(:encounter, EpiViewpoint.R4.Reference) + embeds_one(:code, EpiViewpoint.R4.CodeableConcept) + embeds_one(:subject, EpiViewpoint.R4.Reference) + embeds_one(:data_absent_reason, EpiViewpoint.R4.CodeableConcept) + embeds_one(:text, EpiViewpoint.R4.Narrative) + embeds_one(:body_site, EpiViewpoint.R4.CodeableConcept) + embeds_one(:meta, EpiViewpoint.R4.Meta) + embeds_one(:value_period, EpiViewpoint.R4.Period) + embeds_one(:value_range, EpiViewpoint.R4.Range) + embeds_one(:method, EpiViewpoint.R4.CodeableConcept) + embeds_one(:device, EpiViewpoint.R4.Reference) + embeds_one(:value_codeable_concept, EpiViewpoint.R4.CodeableConcept) # Embed Many - embeds_many(:extension, Epiviewpoint.R4.Extension) - embeds_many(:contained, Epiviewpoint.R4.ResourceList) - embeds_many(:reference_range, Epiviewpoint.R4.Observation.ReferenceRange) - embeds_many(:derived_from, Epiviewpoint.R4.Reference) - embeds_many(:focus, Epiviewpoint.R4.Reference) - embeds_many(:based_on, Epiviewpoint.R4.Reference) - embeds_many(:component, Epiviewpoint.R4.Observation.Component) - embeds_many(:performer, Epiviewpoint.R4.Reference) - embeds_many(:modifier_extension, Epiviewpoint.R4.Extension) - embeds_many(:identifier, Epiviewpoint.R4.Identifier) - embeds_many(:part_of, Epiviewpoint.R4.Reference) - embeds_many(:has_member, Epiviewpoint.R4.Reference) - embeds_many(:category, Epiviewpoint.R4.CodeableConcept) - embeds_many(:note, Epiviewpoint.R4.Annotation) - embeds_many(:interpretation, Epiviewpoint.R4.CodeableConcept) + embeds_many(:extension, EpiViewpoint.R4.Extension) + embeds_many(:contained, EpiViewpoint.R4.ResourceList) + embeds_many(:reference_range, EpiViewpoint.R4.Observation.ReferenceRange) + embeds_many(:derived_from, EpiViewpoint.R4.Reference) + embeds_many(:focus, EpiViewpoint.R4.Reference) + embeds_many(:based_on, EpiViewpoint.R4.Reference) + embeds_many(:component, EpiViewpoint.R4.Observation.Component) + embeds_many(:performer, EpiViewpoint.R4.Reference) + embeds_many(:modifier_extension, EpiViewpoint.R4.Extension) + embeds_many(:identifier, EpiViewpoint.R4.Identifier) + embeds_many(:part_of, EpiViewpoint.R4.Reference) + embeds_many(:has_member, EpiViewpoint.R4.Reference) + embeds_many(:category, EpiViewpoint.R4.CodeableConcept) + embeds_many(:note, EpiViewpoint.R4.Annotation) + embeds_many(:interpretation, EpiViewpoint.R4.CodeableConcept) end def choices("effective") do @@ -139,7 +139,7 @@ defmodule Epiviewpoint.R4.Observation do def choices(_), do: nil - def version_namespace, do: Epiviewpoint.R4 + def version_namespace, do: EpiViewpoint.R4 def version, do: "R4" def path, do: "/Observation" @@ -181,4 +181,4 @@ defmodule Epiviewpoint.R4.Observation do |> cast_embed(:interpretation) |> validate_required(@required_fields) end -end \ No newline at end of file +end diff --git a/lib/epiviewpoint/r4/observation/component.ex b/lib/epiviewpoint/r4/observation/component.ex index a72301fa..7116f81a 100644 --- a/lib/epiviewpoint/r4/observation/component.ex +++ b/lib/epiviewpoint/r4/observation/component.ex @@ -1,4 +1,4 @@ -defmodule Epiviewpoint.R4.Observation.Component do +defmodule EpiViewpoint.R4.Observation.Component do use Ecto.Schema import Ecto.Changeset @@ -21,20 +21,20 @@ defmodule Epiviewpoint.R4.Observation.Component do field(:value_time, :string) # Embed One - embeds_one(:code, Epiviewpoint.R4.CodeableConcept) - embeds_one(:data_absent_reason, Epiviewpoint.R4.CodeableConcept) - embeds_one(:value_codeable_concept, Epiviewpoint.R4.CodeableConcept) - embeds_one(:value_period, Epiviewpoint.R4.Period) - embeds_one(:value_quantity, Epiviewpoint.R4.Quantity) - embeds_one(:value_range, Epiviewpoint.R4.Range) - embeds_one(:value_ratio, Epiviewpoint.R4.Ratio) - embeds_one(:value_sampled_data, Epiviewpoint.R4.SampledData) + embeds_one(:code, EpiViewpoint.R4.CodeableConcept) + embeds_one(:data_absent_reason, EpiViewpoint.R4.CodeableConcept) + embeds_one(:value_codeable_concept, EpiViewpoint.R4.CodeableConcept) + embeds_one(:value_period, EpiViewpoint.R4.Period) + embeds_one(:value_quantity, EpiViewpoint.R4.Quantity) + embeds_one(:value_range, EpiViewpoint.R4.Range) + embeds_one(:value_ratio, EpiViewpoint.R4.Ratio) + embeds_one(:value_sampled_data, EpiViewpoint.R4.SampledData) # Embed Many - embeds_many(:extension, Epiviewpoint.R4.Extension) - embeds_many(:interpretation, Epiviewpoint.R4.CodeableConcept) - embeds_many(:modifier_extension, Epiviewpoint.R4.Extension) - embeds_many(:reference_range, Epiviewpoint.R4.Observation.ReferenceRange) + embeds_many(:extension, EpiViewpoint.R4.Extension) + embeds_many(:interpretation, EpiViewpoint.R4.CodeableConcept) + embeds_many(:modifier_extension, EpiViewpoint.R4.Extension) + embeds_many(:reference_range, EpiViewpoint.R4.Observation.ReferenceRange) end def choices("value") do @@ -77,7 +77,7 @@ defmodule Epiviewpoint.R4.Observation.Component do def choices(_), do: nil - def version_namespace, do: Epiviewpoint.R4 + def version_namespace, do: EpiViewpoint.R4 def version, do: "R4" def changeset(data \\ %__MODULE__{}, attrs) do @@ -97,4 +97,4 @@ defmodule Epiviewpoint.R4.Observation.Component do |> cast_embed(:reference_range) |> validate_required(@required_fields) end -end \ No newline at end of file +end diff --git a/lib/epiviewpoint/r4/observation/reference_range.ex b/lib/epiviewpoint/r4/observation/reference_range.ex index 94f11e5b..c04b7a0f 100644 --- a/lib/epiviewpoint/r4/observation/reference_range.ex +++ b/lib/epiviewpoint/r4/observation/reference_range.ex @@ -1,4 +1,4 @@ -defmodule Epiviewpoint.R4.Observation.ReferenceRange do +defmodule EpiViewpoint.R4.Observation.ReferenceRange do use Ecto.Schema import Ecto.Changeset @@ -13,20 +13,20 @@ defmodule Epiviewpoint.R4.Observation.ReferenceRange do field(:text, :string) # Embed One - embeds_one(:age, Epiviewpoint.R4.Range) - embeds_one(:high, Epiviewpoint.R4.Quantity) - embeds_one(:low, Epiviewpoint.R4.Quantity) - embeds_one(:type, Epiviewpoint.R4.CodeableConcept) + embeds_one(:age, EpiViewpoint.R4.Range) + embeds_one(:high, EpiViewpoint.R4.Quantity) + embeds_one(:low, EpiViewpoint.R4.Quantity) + embeds_one(:type, EpiViewpoint.R4.CodeableConcept) # Embed Many - embeds_many(:applies_to, Epiviewpoint.R4.CodeableConcept) - embeds_many(:extension, Epiviewpoint.R4.Extension) - embeds_many(:modifier_extension, Epiviewpoint.R4.Extension) + embeds_many(:applies_to, EpiViewpoint.R4.CodeableConcept) + embeds_many(:extension, EpiViewpoint.R4.Extension) + embeds_many(:modifier_extension, EpiViewpoint.R4.Extension) end def choices(_), do: nil - def version_namespace, do: Epiviewpoint.R4 + def version_namespace, do: EpiViewpoint.R4 def version, do: "R4" def changeset(data \\ %__MODULE__{}, attrs) do @@ -41,4 +41,4 @@ defmodule Epiviewpoint.R4.Observation.ReferenceRange do |> cast_embed(:modifier_extension) |> validate_required(@required_fields) end -end \ No newline at end of file +end diff --git a/lib/epiviewpoint/r4/organization.ex b/lib/epiviewpoint/r4/organization.ex index ebb3f13c..00bfc582 100644 --- a/lib/epiviewpoint/r4/organization.ex +++ b/lib/epiviewpoint/r4/organization.ex @@ -1,4 +1,4 @@ -defmodule Epiviewpoint.R4.Organization do +defmodule EpiViewpoint.R4.Organization do use Ecto.Schema import Ecto.Changeset @@ -26,25 +26,25 @@ defmodule Epiviewpoint.R4.Organization do field(:alias, {:array, :string}) # Embed One - embeds_one(:meta, Epiviewpoint.R4.Meta) - embeds_one(:part_of, Epiviewpoint.R4.Reference) - embeds_one(:text, Epiviewpoint.R4.Narrative) + embeds_one(:meta, EpiViewpoint.R4.Meta) + embeds_one(:part_of, EpiViewpoint.R4.Reference) + embeds_one(:text, EpiViewpoint.R4.Narrative) # Embed Many - embeds_many(:address, Epiviewpoint.R4.Address) - embeds_many(:contact, Epiviewpoint.R4.Organization.Contact) - embeds_many(:contained, Epiviewpoint.R4.ResourceList) - embeds_many(:endpoint, Epiviewpoint.R4.Reference) - embeds_many(:extension, Epiviewpoint.R4.Extension) - embeds_many(:identifier, Epiviewpoint.R4.Identifier) - embeds_many(:modifier_extension, Epiviewpoint.R4.Extension) - embeds_many(:telecom, Epiviewpoint.R4.ContactPoint) - embeds_many(:type, Epiviewpoint.R4.CodeableConcept) + embeds_many(:address, EpiViewpoint.R4.Address) + embeds_many(:contact, EpiViewpoint.R4.Organization.Contact) + embeds_many(:contained, EpiViewpoint.R4.ResourceList) + embeds_many(:endpoint, EpiViewpoint.R4.Reference) + embeds_many(:extension, EpiViewpoint.R4.Extension) + embeds_many(:identifier, EpiViewpoint.R4.Identifier) + embeds_many(:modifier_extension, EpiViewpoint.R4.Extension) + embeds_many(:telecom, EpiViewpoint.R4.ContactPoint) + embeds_many(:type, EpiViewpoint.R4.CodeableConcept) end def choices(_), do: nil - def version_namespace, do: Epiviewpoint.R4 + def version_namespace, do: EpiViewpoint.R4 def version, do: "R4" def path, do: "/Organization" @@ -65,4 +65,4 @@ defmodule Epiviewpoint.R4.Organization do |> cast_embed(:type) |> validate_required(@required_fields) end -end \ No newline at end of file +end diff --git a/lib/epiviewpoint/r4/organization/contact.ex b/lib/epiviewpoint/r4/organization/contact.ex index a1ba4fa3..06531120 100644 --- a/lib/epiviewpoint/r4/organization/contact.ex +++ b/lib/epiviewpoint/r4/organization/contact.ex @@ -1,4 +1,4 @@ -defmodule Epiviewpoint.R4.Organization.Contact do +defmodule EpiViewpoint.R4.Organization.Contact do use Ecto.Schema import Ecto.Changeset @@ -9,19 +9,19 @@ defmodule Epiviewpoint.R4.Organization.Contact do embedded_schema do # Embed One - embeds_one(:address, Epiviewpoint.R4.Address) - embeds_one(:name, Epiviewpoint.R4.HumanName) - embeds_one(:purpose, Epiviewpoint.R4.CodeableConcept) + embeds_one(:address, EpiViewpoint.R4.Address) + embeds_one(:name, EpiViewpoint.R4.HumanName) + embeds_one(:purpose, EpiViewpoint.R4.CodeableConcept) # Embed Many - embeds_many(:extension, Epiviewpoint.R4.Extension) - embeds_many(:modifier_extension, Epiviewpoint.R4.Extension) - embeds_many(:telecom, Epiviewpoint.R4.ContactPoint) + embeds_many(:extension, EpiViewpoint.R4.Extension) + embeds_many(:modifier_extension, EpiViewpoint.R4.Extension) + embeds_many(:telecom, EpiViewpoint.R4.ContactPoint) end def choices(_), do: nil - def version_namespace, do: Epiviewpoint.R4 + def version_namespace, do: EpiViewpoint.R4 def version, do: "R4" def changeset(data \\ %__MODULE__{}, attrs) do @@ -35,4 +35,4 @@ defmodule Epiviewpoint.R4.Organization.Contact do |> cast_embed(:telecom) |> validate_required(@required_fields) end -end \ No newline at end of file +end diff --git a/lib/epiviewpoint/r4/parameter_definition.ex b/lib/epiviewpoint/r4/parameter_definition.ex index 6c4bd521..5bf17c9f 100644 --- a/lib/epiviewpoint/r4/parameter_definition.ex +++ b/lib/epiviewpoint/r4/parameter_definition.ex @@ -1,4 +1,4 @@ -defmodule Epiviewpoint.R4.ParameterDefinition do +defmodule EpiViewpoint.R4.ParameterDefinition do use Ecto.Schema import Ecto.Changeset @@ -25,12 +25,12 @@ defmodule Epiviewpoint.R4.ParameterDefinition do field(:use, :string) # Embed Many - embeds_many(:extension, Epiviewpoint.R4.Extension) + embeds_many(:extension, EpiViewpoint.R4.Extension) end def choices(_), do: nil - def version_namespace, do: Epiviewpoint.R4 + def version_namespace, do: EpiViewpoint.R4 def version, do: "R4" def changeset(data \\ %__MODULE__{}, attrs) do @@ -39,4 +39,4 @@ defmodule Epiviewpoint.R4.ParameterDefinition do |> cast_embed(:extension) |> validate_required(@required_fields) end -end \ No newline at end of file +end diff --git a/lib/epiviewpoint/r4/patient.ex b/lib/epiviewpoint/r4/patient.ex index e760829b..8318feb8 100644 --- a/lib/epiviewpoint/r4/patient.ex +++ b/lib/epiviewpoint/r4/patient.ex @@ -1,4 +1,4 @@ -defmodule Epiviewpoint.R4.Patient do +defmodule EpiViewpoint.R4.Patient do use Ecto.Schema import Ecto.Changeset @@ -42,24 +42,24 @@ defmodule Epiviewpoint.R4.Patient do ) # Embed One - embeds_one(:marital_status, Epiviewpoint.R4.CodeableConcept) - embeds_one(:managing_organization, Epiviewpoint.R4.Reference) - embeds_one(:text, Epiviewpoint.R4.Narrative) - embeds_one(:meta, Epiviewpoint.R4.Meta) + embeds_one(:marital_status, EpiViewpoint.R4.CodeableConcept) + embeds_one(:managing_organization, EpiViewpoint.R4.Reference) + embeds_one(:text, EpiViewpoint.R4.Narrative) + embeds_one(:meta, EpiViewpoint.R4.Meta) # Embed Many - embeds_many(:photo, Epiviewpoint.R4.Attachment) - embeds_many(:communication, Epiviewpoint.R4.Patient.Communication) - embeds_many(:name, Epiviewpoint.R4.HumanName) - embeds_many(:extension, Epiviewpoint.R4.Extension) - embeds_many(:telecom, Epiviewpoint.R4.ContactPoint) - embeds_many(:contained, Epiviewpoint.R4.ResourceList) - embeds_many(:link, Epiviewpoint.R4.Patient.Link) - embeds_many(:contact, Epiviewpoint.R4.Patient.Contact) - embeds_many(:modifier_extension, Epiviewpoint.R4.Extension) - embeds_many(:identifier, Epiviewpoint.R4.Identifier) - embeds_many(:general_practitioner, Epiviewpoint.R4.Reference) - embeds_many(:address, Epiviewpoint.R4.Address) + embeds_many(:photo, EpiViewpoint.R4.Attachment) + embeds_many(:communication, EpiViewpoint.R4.Patient.Communication) + embeds_many(:name, EpiViewpoint.R4.HumanName) + embeds_many(:extension, EpiViewpoint.R4.Extension) + embeds_many(:telecom, EpiViewpoint.R4.ContactPoint) + embeds_many(:contained, EpiViewpoint.R4.ResourceList) + embeds_many(:link, EpiViewpoint.R4.Patient.Link) + embeds_many(:contact, EpiViewpoint.R4.Patient.Contact) + embeds_many(:modifier_extension, EpiViewpoint.R4.Extension) + embeds_many(:identifier, EpiViewpoint.R4.Identifier) + embeds_many(:general_practitioner, EpiViewpoint.R4.Reference) + embeds_many(:address, EpiViewpoint.R4.Address) end def choices("deceased") do @@ -80,7 +80,7 @@ defmodule Epiviewpoint.R4.Patient do def choices(_), do: nil - def version_namespace, do: Epiviewpoint.R4 + def version_namespace, do: EpiViewpoint.R4 def version, do: "R4" def path, do: "/Patient" @@ -105,4 +105,4 @@ defmodule Epiviewpoint.R4.Patient do |> cast_embed(:address) |> validate_required(@required_fields) end -end \ No newline at end of file +end diff --git a/lib/epiviewpoint/r4/patient/communication.ex b/lib/epiviewpoint/r4/patient/communication.ex index 11aa016b..dee2bebd 100644 --- a/lib/epiviewpoint/r4/patient/communication.ex +++ b/lib/epiviewpoint/r4/patient/communication.ex @@ -1,4 +1,4 @@ -defmodule Epiviewpoint.R4.Patient.Communication do +defmodule EpiViewpoint.R4.Patient.Communication do use Ecto.Schema import Ecto.Changeset @@ -13,16 +13,16 @@ defmodule Epiviewpoint.R4.Patient.Communication do field(:preferred, :boolean) # Embed One - embeds_one(:language, Epiviewpoint.R4.CodeableConcept) + embeds_one(:language, EpiViewpoint.R4.CodeableConcept) # Embed Many - embeds_many(:extension, Epiviewpoint.R4.Extension) - embeds_many(:modifier_extension, Epiviewpoint.R4.Extension) + embeds_many(:extension, EpiViewpoint.R4.Extension) + embeds_many(:modifier_extension, EpiViewpoint.R4.Extension) end def choices(_), do: nil - def version_namespace, do: Epiviewpoint.R4 + def version_namespace, do: EpiViewpoint.R4 def version, do: "R4" def changeset(data \\ %__MODULE__{}, attrs) do @@ -33,4 +33,4 @@ defmodule Epiviewpoint.R4.Patient.Communication do |> cast_embed(:modifier_extension) |> validate_required(@required_fields) end -end \ No newline at end of file +end diff --git a/lib/epiviewpoint/r4/patient/contact.ex b/lib/epiviewpoint/r4/patient/contact.ex index d552e4fa..7faf2b52 100644 --- a/lib/epiviewpoint/r4/patient/contact.ex +++ b/lib/epiviewpoint/r4/patient/contact.ex @@ -1,4 +1,4 @@ -defmodule Epiviewpoint.R4.Patient.Contact do +defmodule EpiViewpoint.R4.Patient.Contact do use Ecto.Schema import Ecto.Changeset @@ -20,21 +20,21 @@ defmodule Epiviewpoint.R4.Patient.Contact do ) # Embed One - embeds_one(:address, Epiviewpoint.R4.Address) - embeds_one(:name, Epiviewpoint.R4.HumanName) - embeds_one(:organization, Epiviewpoint.R4.Reference) - embeds_one(:period, Epiviewpoint.R4.Period) + embeds_one(:address, EpiViewpoint.R4.Address) + embeds_one(:name, EpiViewpoint.R4.HumanName) + embeds_one(:organization, EpiViewpoint.R4.Reference) + embeds_one(:period, EpiViewpoint.R4.Period) # Embed Many - embeds_many(:extension, Epiviewpoint.R4.Extension) - embeds_many(:modifier_extension, Epiviewpoint.R4.Extension) - embeds_many(:relationship, Epiviewpoint.R4.CodeableConcept) - embeds_many(:telecom, Epiviewpoint.R4.ContactPoint) + embeds_many(:extension, EpiViewpoint.R4.Extension) + embeds_many(:modifier_extension, EpiViewpoint.R4.Extension) + embeds_many(:relationship, EpiViewpoint.R4.CodeableConcept) + embeds_many(:telecom, EpiViewpoint.R4.ContactPoint) end def choices(_), do: nil - def version_namespace, do: Epiviewpoint.R4 + def version_namespace, do: EpiViewpoint.R4 def version, do: "R4" def changeset(data \\ %__MODULE__{}, attrs) do @@ -50,4 +50,4 @@ defmodule Epiviewpoint.R4.Patient.Contact do |> cast_embed(:telecom) |> validate_required(@required_fields) end -end \ No newline at end of file +end diff --git a/lib/epiviewpoint/r4/patient/link.ex b/lib/epiviewpoint/r4/patient/link.ex index 6aa952d1..8798d8c7 100644 --- a/lib/epiviewpoint/r4/patient/link.ex +++ b/lib/epiviewpoint/r4/patient/link.ex @@ -1,4 +1,4 @@ -defmodule Epiviewpoint.R4.Patient.Link do +defmodule EpiViewpoint.R4.Patient.Link do use Ecto.Schema import Ecto.Changeset @@ -20,16 +20,16 @@ defmodule Epiviewpoint.R4.Patient.Link do ) # Embed One - embeds_one(:other, Epiviewpoint.R4.Reference) + embeds_one(:other, EpiViewpoint.R4.Reference) # Embed Many - embeds_many(:extension, Epiviewpoint.R4.Extension) - embeds_many(:modifier_extension, Epiviewpoint.R4.Extension) + embeds_many(:extension, EpiViewpoint.R4.Extension) + embeds_many(:modifier_extension, EpiViewpoint.R4.Extension) end def choices(_), do: nil - def version_namespace, do: Epiviewpoint.R4 + def version_namespace, do: EpiViewpoint.R4 def version, do: "R4" def changeset(data \\ %__MODULE__{}, attrs) do @@ -40,4 +40,4 @@ defmodule Epiviewpoint.R4.Patient.Link do |> cast_embed(:modifier_extension) |> validate_required(@required_fields) end -end \ No newline at end of file +end diff --git a/lib/epiviewpoint/r4/period.ex b/lib/epiviewpoint/r4/period.ex index b4de40d4..9d083edc 100644 --- a/lib/epiviewpoint/r4/period.ex +++ b/lib/epiviewpoint/r4/period.ex @@ -1,4 +1,4 @@ -defmodule Epiviewpoint.R4.Period do +defmodule EpiViewpoint.R4.Period do use Ecto.Schema import Ecto.Changeset @@ -15,12 +15,12 @@ defmodule Epiviewpoint.R4.Period do field(:start, :utc_datetime_usec) # Embed Many - embeds_many(:extension, Epiviewpoint.R4.Extension) + embeds_many(:extension, EpiViewpoint.R4.Extension) end def choices(_), do: nil - def version_namespace, do: Epiviewpoint.R4 + def version_namespace, do: EpiViewpoint.R4 def version, do: "R4" def changeset(data \\ %__MODULE__{}, attrs) do @@ -29,4 +29,4 @@ defmodule Epiviewpoint.R4.Period do |> cast_embed(:extension) |> validate_required(@required_fields) end -end \ No newline at end of file +end diff --git a/lib/epiviewpoint/r4/quantity.ex b/lib/epiviewpoint/r4/quantity.ex index 22aab8b5..5db0642c 100644 --- a/lib/epiviewpoint/r4/quantity.ex +++ b/lib/epiviewpoint/r4/quantity.ex @@ -1,4 +1,4 @@ -defmodule Epiviewpoint.R4.Quantity do +defmodule EpiViewpoint.R4.Quantity do use Ecto.Schema import Ecto.Changeset @@ -30,12 +30,12 @@ defmodule Epiviewpoint.R4.Quantity do ) # Embed Many - embeds_many(:extension, Epiviewpoint.R4.Extension) + embeds_many(:extension, EpiViewpoint.R4.Extension) end def choices(_), do: nil - def version_namespace, do: Epiviewpoint.R4 + def version_namespace, do: EpiViewpoint.R4 def version, do: "R4" def changeset(data \\ %__MODULE__{}, attrs) do @@ -44,4 +44,4 @@ defmodule Epiviewpoint.R4.Quantity do |> cast_embed(:extension) |> validate_required(@required_fields) end -end \ No newline at end of file +end diff --git a/lib/epiviewpoint/r4/range.ex b/lib/epiviewpoint/r4/range.ex index d51e9c8e..eef453e4 100644 --- a/lib/epiviewpoint/r4/range.ex +++ b/lib/epiviewpoint/r4/range.ex @@ -1,4 +1,4 @@ -defmodule Epiviewpoint.R4.Range do +defmodule EpiViewpoint.R4.Range do use Ecto.Schema import Ecto.Changeset @@ -9,16 +9,16 @@ defmodule Epiviewpoint.R4.Range do embedded_schema do # Embed One - embeds_one(:high, Epiviewpoint.R4.Quantity) - embeds_one(:low, Epiviewpoint.R4.Quantity) + embeds_one(:high, EpiViewpoint.R4.Quantity) + embeds_one(:low, EpiViewpoint.R4.Quantity) # Embed Many - embeds_many(:extension, Epiviewpoint.R4.Extension) + embeds_many(:extension, EpiViewpoint.R4.Extension) end def choices(_), do: nil - def version_namespace, do: Epiviewpoint.R4 + def version_namespace, do: EpiViewpoint.R4 def version, do: "R4" def changeset(data \\ %__MODULE__{}, attrs) do @@ -29,4 +29,4 @@ defmodule Epiviewpoint.R4.Range do |> cast_embed(:extension) |> validate_required(@required_fields) end -end \ No newline at end of file +end diff --git a/lib/epiviewpoint/r4/ratio.ex b/lib/epiviewpoint/r4/ratio.ex index 50a3790f..2b61c8c4 100644 --- a/lib/epiviewpoint/r4/ratio.ex +++ b/lib/epiviewpoint/r4/ratio.ex @@ -1,4 +1,4 @@ -defmodule Epiviewpoint.R4.Ratio do +defmodule EpiViewpoint.R4.Ratio do use Ecto.Schema import Ecto.Changeset @@ -9,16 +9,16 @@ defmodule Epiviewpoint.R4.Ratio do embedded_schema do # Embed One - embeds_one(:denominator, Epiviewpoint.R4.Quantity) - embeds_one(:numerator, Epiviewpoint.R4.Quantity) + embeds_one(:denominator, EpiViewpoint.R4.Quantity) + embeds_one(:numerator, EpiViewpoint.R4.Quantity) # Embed Many - embeds_many(:extension, Epiviewpoint.R4.Extension) + embeds_many(:extension, EpiViewpoint.R4.Extension) end def choices(_), do: nil - def version_namespace, do: Epiviewpoint.R4 + def version_namespace, do: EpiViewpoint.R4 def version, do: "R4" def changeset(data \\ %__MODULE__{}, attrs) do @@ -29,4 +29,4 @@ defmodule Epiviewpoint.R4.Ratio do |> cast_embed(:extension) |> validate_required(@required_fields) end -end \ No newline at end of file +end diff --git a/lib/epiviewpoint/r4/reference.ex b/lib/epiviewpoint/r4/reference.ex index 92b83c7b..8eeb1625 100644 --- a/lib/epiviewpoint/r4/reference.ex +++ b/lib/epiviewpoint/r4/reference.ex @@ -1,4 +1,4 @@ -defmodule Epiviewpoint.R4.Reference do +defmodule EpiViewpoint.R4.Reference do use Ecto.Schema import Ecto.Changeset @@ -17,15 +17,15 @@ defmodule Epiviewpoint.R4.Reference do field(:type, :string) # Embed One - embeds_one(:identifier, Epiviewpoint.R4.Identifier) + embeds_one(:identifier, EpiViewpoint.R4.Identifier) # Embed Many - embeds_many(:extension, Epiviewpoint.R4.Extension) + embeds_many(:extension, EpiViewpoint.R4.Extension) end def choices(_), do: nil - def version_namespace, do: Epiviewpoint.R4 + def version_namespace, do: EpiViewpoint.R4 def version, do: "R4" def changeset(data \\ %__MODULE__{}, attrs) do @@ -35,4 +35,4 @@ defmodule Epiviewpoint.R4.Reference do |> cast_embed(:extension) |> validate_required(@required_fields) end -end \ No newline at end of file +end diff --git a/lib/epiviewpoint/r4/related_artifact.ex b/lib/epiviewpoint/r4/related_artifact.ex index faabd573..ce9afc08 100644 --- a/lib/epiviewpoint/r4/related_artifact.ex +++ b/lib/epiviewpoint/r4/related_artifact.ex @@ -1,4 +1,4 @@ -defmodule Epiviewpoint.R4.RelatedArtifact do +defmodule EpiViewpoint.R4.RelatedArtifact do use Ecto.Schema import Ecto.Changeset @@ -36,15 +36,15 @@ defmodule Epiviewpoint.R4.RelatedArtifact do ) # Embed One - embeds_one(:document, Epiviewpoint.R4.Attachment) + embeds_one(:document, EpiViewpoint.R4.Attachment) # Embed Many - embeds_many(:extension, Epiviewpoint.R4.Extension) + embeds_many(:extension, EpiViewpoint.R4.Extension) end def choices(_), do: nil - def version_namespace, do: Epiviewpoint.R4 + def version_namespace, do: EpiViewpoint.R4 def version, do: "R4" def changeset(data \\ %__MODULE__{}, attrs) do @@ -54,4 +54,4 @@ defmodule Epiviewpoint.R4.RelatedArtifact do |> cast_embed(:extension) |> validate_required(@required_fields) end -end \ No newline at end of file +end diff --git a/lib/epiviewpoint/r4/resource_list.ex b/lib/epiviewpoint/r4/resource_list.ex index 1fb62b10..530cc946 100644 --- a/lib/epiviewpoint/r4/resource_list.ex +++ b/lib/epiviewpoint/r4/resource_list.ex @@ -1,4 +1,4 @@ -defmodule Epiviewpoint.R4.ResourceList do +defmodule EpiViewpoint.R4.ResourceList do use Ecto.Schema import Ecto.Changeset @@ -10,7 +10,7 @@ defmodule Epiviewpoint.R4.ResourceList do def choices(_), do: nil - def version_namespace, do: Epiviewpoint.R4 + def version_namespace, do: EpiViewpoint.R4 def version, do: "R4" def changeset(data \\ %__MODULE__{}, attrs) do @@ -18,4 +18,4 @@ defmodule Epiviewpoint.R4.ResourceList do |> cast(attrs, @fields) |> validate_required(@required_fields) end -end \ No newline at end of file +end diff --git a/lib/epiviewpoint/r4/sampled_data.ex b/lib/epiviewpoint/r4/sampled_data.ex index 4ee45d48..2625bc49 100644 --- a/lib/epiviewpoint/r4/sampled_data.ex +++ b/lib/epiviewpoint/r4/sampled_data.ex @@ -1,4 +1,4 @@ -defmodule Epiviewpoint.R4.SampledData do +defmodule EpiViewpoint.R4.SampledData do use Ecto.Schema import Ecto.Changeset @@ -23,15 +23,15 @@ defmodule Epiviewpoint.R4.SampledData do field(:upper_limit, :decimal) # Embed One - embeds_one(:origin, Epiviewpoint.R4.Quantity) + embeds_one(:origin, EpiViewpoint.R4.Quantity) # Embed Many - embeds_many(:extension, Epiviewpoint.R4.Extension) + embeds_many(:extension, EpiViewpoint.R4.Extension) end def choices(_), do: nil - def version_namespace, do: Epiviewpoint.R4 + def version_namespace, do: EpiViewpoint.R4 def version, do: "R4" def changeset(data \\ %__MODULE__{}, attrs) do @@ -41,4 +41,4 @@ defmodule Epiviewpoint.R4.SampledData do |> cast_embed(:extension) |> validate_required(@required_fields) end -end \ No newline at end of file +end diff --git a/lib/epiviewpoint/r4/signature.ex b/lib/epiviewpoint/r4/signature.ex index 7dd9298d..d25a7213 100644 --- a/lib/epiviewpoint/r4/signature.ex +++ b/lib/epiviewpoint/r4/signature.ex @@ -1,4 +1,4 @@ -defmodule Epiviewpoint.R4.Signature do +defmodule EpiViewpoint.R4.Signature do use Ecto.Schema import Ecto.Changeset @@ -19,17 +19,17 @@ defmodule Epiviewpoint.R4.Signature do field(:when, :utc_datetime_usec) # Embed One - embeds_one(:on_behalf_of, Epiviewpoint.R4.Reference) - embeds_one(:who, Epiviewpoint.R4.Reference) + embeds_one(:on_behalf_of, EpiViewpoint.R4.Reference) + embeds_one(:who, EpiViewpoint.R4.Reference) # Embed Many - embeds_many(:extension, Epiviewpoint.R4.Extension) - embeds_many(:type, Epiviewpoint.R4.Coding) + embeds_many(:extension, EpiViewpoint.R4.Extension) + embeds_many(:type, EpiViewpoint.R4.Coding) end def choices(_), do: nil - def version_namespace, do: Epiviewpoint.R4 + def version_namespace, do: EpiViewpoint.R4 def version, do: "R4" def changeset(data \\ %__MODULE__{}, attrs) do @@ -41,4 +41,4 @@ defmodule Epiviewpoint.R4.Signature do |> cast_embed(:type) |> validate_required(@required_fields) end -end \ No newline at end of file +end diff --git a/lib/epiviewpoint/r4/timing.ex b/lib/epiviewpoint/r4/timing.ex index 77524b79..3c4a5394 100644 --- a/lib/epiviewpoint/r4/timing.ex +++ b/lib/epiviewpoint/r4/timing.ex @@ -1,4 +1,4 @@ -defmodule Epiviewpoint.R4.Timing do +defmodule EpiViewpoint.R4.Timing do use Ecto.Schema import Ecto.Changeset @@ -12,17 +12,17 @@ defmodule Epiviewpoint.R4.Timing do field(:event, {:array, :utc_datetime_usec}) # Embed One - embeds_one(:code, Epiviewpoint.R4.CodeableConcept) - embeds_one(:repeat, Epiviewpoint.R4.Timing.Repeat) + embeds_one(:code, EpiViewpoint.R4.CodeableConcept) + embeds_one(:repeat, EpiViewpoint.R4.Timing.Repeat) # Embed Many - embeds_many(:extension, Epiviewpoint.R4.Extension) - embeds_many(:modifier_extension, Epiviewpoint.R4.Extension) + embeds_many(:extension, EpiViewpoint.R4.Extension) + embeds_many(:modifier_extension, EpiViewpoint.R4.Extension) end def choices(_), do: nil - def version_namespace, do: Epiviewpoint.R4 + def version_namespace, do: EpiViewpoint.R4 def version, do: "R4" def changeset(data \\ %__MODULE__{}, attrs) do @@ -34,4 +34,4 @@ defmodule Epiviewpoint.R4.Timing do |> cast_embed(:modifier_extension) |> validate_required(@required_fields) end -end \ No newline at end of file +end diff --git a/lib/epiviewpoint/r4/timing/repeat.ex b/lib/epiviewpoint/r4/timing/repeat.ex index 321f3071..5ecf31ed 100644 --- a/lib/epiviewpoint/r4/timing/repeat.ex +++ b/lib/epiviewpoint/r4/timing/repeat.ex @@ -1,4 +1,4 @@ -defmodule Epiviewpoint.R4.Timing.Repeat do +defmodule EpiViewpoint.R4.Timing.Repeat do use Ecto.Schema import Ecto.Changeset @@ -63,13 +63,13 @@ defmodule Epiviewpoint.R4.Timing.Repeat do ) # Embed One - embeds_one(:bounds_duration, Epiviewpoint.R4.Duration) - embeds_one(:bounds_range, Epiviewpoint.R4.Range) - embeds_one(:bounds_period, Epiviewpoint.R4.Period) + embeds_one(:bounds_duration, EpiViewpoint.R4.Duration) + embeds_one(:bounds_range, EpiViewpoint.R4.Range) + embeds_one(:bounds_period, EpiViewpoint.R4.Period) # Embed Many - embeds_many(:extension, Epiviewpoint.R4.Extension) - embeds_many(:modifier_extension, Epiviewpoint.R4.Extension) + embeds_many(:extension, EpiViewpoint.R4.Extension) + embeds_many(:modifier_extension, EpiViewpoint.R4.Extension) end def choices("bounds") do @@ -84,7 +84,7 @@ defmodule Epiviewpoint.R4.Timing.Repeat do def choices(_), do: nil - def version_namespace, do: Epiviewpoint.R4 + def version_namespace, do: EpiViewpoint.R4 def version, do: "R4" def changeset(data \\ %__MODULE__{}, attrs) do @@ -97,4 +97,4 @@ defmodule Epiviewpoint.R4.Timing.Repeat do |> cast_embed(:modifier_extension) |> validate_required(@required_fields) end -end \ No newline at end of file +end diff --git a/lib/epiviewpoint/r4/trigger_definition.ex b/lib/epiviewpoint/r4/trigger_definition.ex index febc5075..9364e4fd 100644 --- a/lib/epiviewpoint/r4/trigger_definition.ex +++ b/lib/epiviewpoint/r4/trigger_definition.ex @@ -1,4 +1,4 @@ -defmodule Epiviewpoint.R4.TriggerDefinition do +defmodule EpiViewpoint.R4.TriggerDefinition do use Ecto.Schema import Ecto.Changeset @@ -32,13 +32,13 @@ defmodule Epiviewpoint.R4.TriggerDefinition do ) # Embed One - embeds_one(:condition, Epiviewpoint.R4.Expression) - embeds_one(:timing_reference, Epiviewpoint.R4.Reference) - embeds_one(:timing_timing, Epiviewpoint.R4.Timing) + embeds_one(:condition, EpiViewpoint.R4.Expression) + embeds_one(:timing_reference, EpiViewpoint.R4.Reference) + embeds_one(:timing_timing, EpiViewpoint.R4.Timing) # Embed Many - embeds_many(:data, Epiviewpoint.R4.DataRequirement) - embeds_many(:extension, Epiviewpoint.R4.Extension) + embeds_many(:data, EpiViewpoint.R4.DataRequirement) + embeds_many(:extension, EpiViewpoint.R4.Extension) end def choices("timing") do @@ -55,7 +55,7 @@ defmodule Epiviewpoint.R4.TriggerDefinition do def choices(_), do: nil - def version_namespace, do: Epiviewpoint.R4 + def version_namespace, do: EpiViewpoint.R4 def version, do: "R4" def changeset(data \\ %__MODULE__{}, attrs) do @@ -68,4 +68,4 @@ defmodule Epiviewpoint.R4.TriggerDefinition do |> cast_embed(:extension) |> validate_required(@required_fields) end -end \ No newline at end of file +end diff --git a/lib/epiviewpoint/r4/usage_context.ex b/lib/epiviewpoint/r4/usage_context.ex index b96c15a6..31c7f2f1 100644 --- a/lib/epiviewpoint/r4/usage_context.ex +++ b/lib/epiviewpoint/r4/usage_context.ex @@ -1,4 +1,4 @@ -defmodule Epiviewpoint.R4.UsageContext do +defmodule EpiViewpoint.R4.UsageContext do use Ecto.Schema import Ecto.Changeset @@ -9,14 +9,14 @@ defmodule Epiviewpoint.R4.UsageContext do embedded_schema do # Embed One - embeds_one(:code, Epiviewpoint.R4.Coding) - embeds_one(:value_codeable_concept, Epiviewpoint.R4.CodeableConcept) - embeds_one(:value_quantity, Epiviewpoint.R4.Quantity) - embeds_one(:value_range, Epiviewpoint.R4.Range) - embeds_one(:value_reference, Epiviewpoint.R4.Reference) + embeds_one(:code, EpiViewpoint.R4.Coding) + embeds_one(:value_codeable_concept, EpiViewpoint.R4.CodeableConcept) + embeds_one(:value_quantity, EpiViewpoint.R4.Quantity) + embeds_one(:value_range, EpiViewpoint.R4.Range) + embeds_one(:value_reference, EpiViewpoint.R4.Reference) # Embed Many - embeds_many(:extension, Epiviewpoint.R4.Extension) + embeds_many(:extension, EpiViewpoint.R4.Extension) end def choices("value") do @@ -33,7 +33,7 @@ defmodule Epiviewpoint.R4.UsageContext do def choices(_), do: nil - def version_namespace, do: Epiviewpoint.R4 + def version_namespace, do: EpiViewpoint.R4 def version, do: "R4" def changeset(data \\ %__MODULE__{}, attrs) do @@ -47,4 +47,4 @@ defmodule Epiviewpoint.R4.UsageContext do |> cast_embed(:extension) |> validate_required(@required_fields) end -end \ No newline at end of file +end diff --git a/lib/epiviewpoint_web/controllers/import_controller.ex b/lib/epiviewpoint_web/controllers/import_controller.ex index a8efc1c7..17fe7ca1 100644 --- a/lib/epiviewpoint_web/controllers/import_controller.ex +++ b/lib/epiviewpoint_web/controllers/import_controller.ex @@ -42,7 +42,6 @@ defmodule EpiViewpointWeb.ImportController do end def create_bulk_fhir(conn, %{"files" => plug_uploads}) do - result = UploadedFile.from_plug_uploads(plug_uploads) |> Cases.import_bulk_fhir_lab_results(conn.assigns.current_user) case result do @@ -63,10 +62,8 @@ defmodule EpiViewpointWeb.ImportController do |> Session.set_import_error_message(user_readable_message) |> redirect(to: ~p"/import/start") end - end - def show(conn, _params) do conn |> assign_defaults(@common_assigns) diff --git a/lib/epiviewpoint_web/live/import_live.html.heex b/lib/epiviewpoint_web/live/import_live.html.heex index 96ff9085..70a02288 100644 --- a/lib/epiviewpoint_web/live/import_live.html.heex +++ b/lib/epiviewpoint_web/live/import_live.html.heex @@ -17,7 +17,7 @@

Choose bulk FHIR NDJSON files from your computer, click “Upload”, and then wait for the file to upload.

- <%= form_tag("/import/upload_bulk_fhir", multipart: true, method: :post) %> "Alice", + "last_name" => "Ant", + "dob" => "01/02/1970", + "sample_date" => "06/01/2020", + "result_date" => "06/03/2020", + "result" => "positive" + }, + %{ + "first_name" => "Billy", + "last_name" => "Bat", + "dob" => "03/04/1990", + "sample_date" => "06/06/2020", + "result_date" => "06/07/2020", + "result" => "negative" + } + ] + + assert {:ok, result} = + DataFile.read(input, :bulk_fhir, &Function.identity/1, + required: ~w{first_name last_name dob sample_date result_date result}, + optional: ~w{} + ) + + assert result == [ + %{ + "first_name" => "Alice", + "last_name" => "Ant", + "dob" => "01/02/1970", + "sample_date" => "06/01/2020", + "result_date" => "06/03/2020", + "result" => "positive" + }, + %{ + "first_name" => "Billy", + "last_name" => "Bat", + "dob" => "03/04/1990", + "sample_date" => "06/06/2020", + "result_date" => "06/07/2020", + "result" => "negative" + } + ] + end + + test "ignores unspecified headers in bulk FHIR" do + input = [ + %{ + "column_a" => "value_a", + "column_b" => "value_b", + "column_c" => "value_c" + } + ] + + assert {:ok, result} = + DataFile.read(input, :bulk_fhir, &Function.identity/1, + required: ~w{column_a}, + optional: ~w{column_b} + ) + + assert result == [%{"column_a" => "value_a", "column_b" => "value_b"}] + end + + test "fails if required header is missing in bulk FHIR" do + input = [ + %{ + "column_a" => "value_a", + "column_b" => "value_b" + } + ] + + assert {:error, :missing_headers, missing} = + DataFile.read(input, :bulk_fhir, &Function.identity/1, + required: ~w{column_a column_b column_c column_d}, + optional: ~w{} + ) + + assert Enum.sort(missing) == ["column_c", "column_d"] + end + + test "allows optional headers in bulk FHIR" do + input = [ + %{ + "column_a" => "value_a", + "column_b" => "value_b", + "optional_c" => "value_c" + } + ] + + assert {:ok, result} = + DataFile.read(input, :bulk_fhir, &Function.identity/1, + required: ~w{column_a column_b}, + optional: ~w{optional_c optional_d} + ) + + assert result == [%{"column_a" => "value_a", "column_b" => "value_b", "optional_c" => "value_c"}] + end + + test "header transformer transforms headers in bulk FHIR" do + input = [ + %{ + "first_name" => "Alice", + "last_name" => "Ant" + }, + %{ + "first_name" => "Billy", + "last_name" => "Bat" + } + ] + + assert {:ok, result} = + DataFile.read(input, :bulk_fhir, fn headers -> Enum.map(headers, &String.upcase/1) end, + required: ~w{FIRST_NAME LAST_NAME}, + optional: ~w{} + ) + + assert result == [ + %{ + "FIRST_NAME" => "Alice", + "LAST_NAME" => "Ant" + }, + %{ + "FIRST_NAME" => "Billy", + "LAST_NAME" => "Bat" + } + ] + end end end diff --git a/test/epiviewpoint_web/controllers/import_controller_test.exs b/test/epiviewpoint_web/controllers/import_controller_test.exs index fedb422a..441b6f7e 100644 --- a/test/epiviewpoint_web/controllers/import_controller_test.exs +++ b/test/epiviewpoint_web/controllers/import_controller_test.exs @@ -129,6 +129,46 @@ defmodule EpiViewpointWeb.ImportControllerTest do assert conn |> redirected_to() == "/import/start" assert "Invalid mm-dd-yyyy format: 06/02/bb" = Session.get_import_error_message(conn) end + + test "accepts multiple NDJSON files for bulk FHIR upload", %{conn: conn, tmp_dir: tmp_dir} do + patient_data = """ + {"resourceType":"Patient","id":"10000","meta":{"profile":["http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient"]},"identifier":[{"system":"urn:example:person_tid","value":"alice"}],"name":[{"family":"Testuser","given":["Alice"]}],"birthDate":"1970-01-01","telecom":[{"system":"phone","value":"1111111000"}],"address":[{"line":["1234 Test St"],"city":"City","state":"TS","postalCode":"00000"}],"gender":"female","extension":[{"url":"http://hl7.org/fhir/us/core/StructureDefinition/us-core-race","extension":[{"url":"ombCategory","valueCoding":{"system":"urn:oid:2.16.840.1.113883.6.238","code":"2106-3","display":"White"}},{"url":"text","valueString":"White"}]},{"url":"http://hl7.org/fhir/us/core/StructureDefinition/us-core-ethnicity","extension":[{"url":"ombCategory","valueCoding":{"system":"urn:oid:2.16.840.1.113883.6.238","code":"2186-5","display":"Not Hispanic or Latino"}},{"url":"text","valueString":"Not Hispanic or Latino"}]},{"url":"http://hl7.org/fhir/StructureDefinition/patient-occupation","valueString":"Doctor"}]} + {"resourceType":"Patient","id":"10004","meta":{"profile":["http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient"]},"identifier":[{"system":"urn:example:person_tid","value":"billy"}],"name":[{"family":"Testuser","given":["Billy"]}],"birthDate":"1971-01-01","telecom":[{"system":"phone","value":"1111111004"}],"address":[{"line":["1234 Test St"],"city":"City","state":"TS","postalCode":"00000"}],"gender":"female","extension":[{"url":"http://hl7.org/fhir/us/core/StructureDefinition/us-core-race","extension":[{"url":"ombCategory","valueCoding":{"system":"urn:oid:2.16.840.1.113883.6.238","code":"2106-3","display":"White"}},{"url":"text","valueString":"White"}]},{"url":"http://hl7.org/fhir/us/core/StructureDefinition/us-core-ethnicity","extension":[{"url":"ombCategory","valueCoding":{"system":"urn:oid:2.16.840.1.113883.6.238","code":"2186-5","display":"Not Hispanic or Latino"}},{"url":"text","valueString":"Not Hispanic or Latino"}]},{"url":"http://hl7.org/fhir/StructureDefinition/patient-occupation","valueString":"Doctor"}]} + """ + + observation_data = """ + {"resourceType":"Observation","id":"alice-result-1","meta":{"profile":["http://hl7.org/fhir/us/core/StructureDefinition/us-core-observation-lab"]},"extension":[{"url":"http://hl7.org/fhir/StructureDefinition/datereportedtolhd","valueDate":"08/05/2020"}],"category":[{"coding":[{"system":"http://terminology.hl7.org/CodeSystem/observation-category","code":"laboratory","display":"Laboratory"}]}],"status":"final","code":{"text":"TestTest"},"subject":{"reference":"Patient/10000"},"effectiveDateTime":"08/01/2020","issued":"2020-08-03T00:00:00Z","performer":[{"reference":"Organization/city-hospital-lab"}],"valueCodeableConcept":{"coding":[{"system":"http://snomed.info/sct","code":"10828004","display":"Positive"}]},"interpretation":[{"coding":[{"system":"http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation","code":"POS","display":"Positive"}]}]} + {"resourceType":"Observation","id":"billy-result-1","meta":{"profile":["http://hl7.org/fhir/us/core/StructureDefinition/us-core-observation-lab"]},"extension":[{"url":"http://hl7.org/fhir/StructureDefinition/datereportedtolhd","valueDate":"08/05/2020"}],"category":[{"coding":[{"system":"http://terminology.hl7.org/CodeSystem/observation-category","code":"laboratory","display":"Laboratory"}]}],"status":"final","code":{"text":"TestTest"},"subject":{"reference":"Patient/10004"},"effectiveDateTime":"08/02/2020","issued":"2020-08-04T00:00:00Z","performer":[{"reference":"Organization/city-hospital-lab"}],"valueCodeableConcept":{"coding":[{"system":"http://snomed.info/sct","code":"260385009","display":"Negative"}]},"interpretation":[{"coding":[{"system":"http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation","code":"NEG","display":"Negative"}]}]} + """ + + organization_data = """ + {"resourceType":"Organization","id":"org1","name":"Lab Co South"} + {"resourceType":"Organization","id":"org2","name":"City Hospital Lab"} + """ + + patient_file_path = Tempfile.write_ndjson!(patient_data, tmp_dir) + observation_file_path = Tempfile.write_ndjson!(observation_data, tmp_dir) + organization_file_path = Tempfile.write_ndjson!(organization_data, tmp_dir) + + conn = + conn + |> post(~p"/import/upload_bulk_fhir", %{ + "files" => [ + %Plug.Upload{path: patient_file_path, filename: "Patient.ndjson"}, + %Plug.Upload{path: observation_file_path, filename: "Observation.ndjson"}, + %Plug.Upload{path: organization_file_path, filename: "Organization.ndjson"} + ] + }) + + assert conn |> redirected_to() == "/import/complete" + + assert %EpiViewpoint.Cases.Import.ImportInfo{ + imported_lab_result_count: 2, + imported_person_count: 2, + total_lab_result_count: 2, + total_person_count: 2 + } = Session.get_last_file_import_info(conn) + end end describe "show" do From 3106aea0a5448f9aeef96ccd4f1b3c1b0285a9f6 Mon Sep 17 00:00:00 2001 From: Lei Zhou Date: Fri, 6 Sep 2024 17:16:18 -0400 Subject: [PATCH 3/8] code refactoring --- lib/epiviewpoint/bulk_fhir_parser.ex | 249 +++++++++++++++------------ lib/epiviewpoint/cases/import.ex | 5 +- 2 files changed, 145 insertions(+), 109 deletions(-) diff --git a/lib/epiviewpoint/bulk_fhir_parser.ex b/lib/epiviewpoint/bulk_fhir_parser.ex index 89caeb1e..e4641906 100644 --- a/lib/epiviewpoint/bulk_fhir_parser.ex +++ b/lib/epiviewpoint/bulk_fhir_parser.ex @@ -1,122 +1,155 @@ defmodule EpiViewpoint.BulkFhirParser do def parse_bulk_fhir(file_list) do - contents = file_list |> Enum.map(& &1.contents) |> List.to_string() + with {:ok, contents} <- combine_contents(file_list), + {:ok, resources} <- load_resources(file_list), + {:ok, extracted} <- extract_resources(resources), + {:ok, joined} <- join_resources(extracted) do + {:ok, to_map(joined, contents)} + end + end - file_list - |> load_resources() - |> extract_resources() - |> join_resources() - |> to_map(contents) + defp combine_contents(file_list) do + contents = file_list |> Enum.map(& &1.contents) |> List.to_string() + {:ok, contents} end - def load_resources(file_list) do - file_list - |> Enum.reduce(%{}, fn file, acc -> - resources = load_resource_file(file.contents) + defp load_resources(file_list) do + result = Enum.reduce_while(file_list, {:ok, %{}}, fn file, {:ok, acc} -> + case load_resource_file(file.contents) do + {:ok, resources} -> {:cont, {:ok, group_resources(resources, acc)}} + {:error, reason} -> {:halt, {:error, reason}} + end + end) - Enum.reduce(resources, acc, fn resource, inner_acc -> - type = resource.resource_type - Map.update(inner_acc, type, [resource], &[resource | &1]) - end) + case result do + {:ok, resources} -> {:ok, resources} + {:error, reason} -> {:error, reason} + end + end + + defp group_resources(resources, acc) do + Enum.reduce(resources, acc, fn resource, acc -> + Map.update(acc, resource.resource_type, [resource], &[resource | &1]) end) end - def load_resource_file(file_content) do - file_content + defp load_resource_file(file_content) do + result = file_content |> String.split("\n") |> Stream.map(&String.trim/1) - |> Stream.filter(&(&1 != "")) - |> Stream.map(&json_to_kindle_schema(&1, "EpiViewpoint.R4")) + |> Stream.filter(&filter_empty_lines/1) + |> Stream.map(&json_to_kindle_schema/1) |> Enum.to_list() + + {:ok, result} + rescue + e -> {:error, "Failed to load resource file: #{inspect(e)}"} end - def extract_resources(resources) do - resources - |> Enum.reduce(%{}, fn {resource_type, resource_list}, acc -> - Map.put(acc, resource_type, extract_resource(resource_type, resource_list)) - end) + defp json_to_kindle_schema(json) do + case Jason.decode(json) do + {:ok, map} -> Kindling.Converter.convert("EpiViewpoint.R4", map) + {:error, reason} -> {:error, "Failed to decode JSON: #{inspect(reason)}"} + end end - def extract_resource("Patient", resource_list) do - resource_list - |> Enum.map(fn - %EpiViewpoint.R4.Patient{ - id: caseid, - identifier: [%EpiViewpoint.R4.Identifier{value: person_tid}], - name: [%EpiViewpoint.R4.HumanName{given: [search_firstname], family: search_lastname}], - birth_date: dateofbirth, - gender: sex, - address: [ - %EpiViewpoint.R4.Address{ - line: [diagaddress_street1], - city: diagaddress_city, - state: diagaddress_state, - postal_code: diagaddress_zip - } - ], - telecom: [%EpiViewpoint.R4.ContactPoint{value: phonenumber}], - extension: extensions - } = _resource -> - %{ - caseid: caseid, - person_tid: person_tid, - search_firstname: search_firstname, - search_lastname: search_lastname, - dateofbirth: Calendar.strftime(dateofbirth, "%m/%d/%Y"), - sex: sex |> to_string() |> String.capitalize(), - diagaddress_street1: diagaddress_street1, - diagaddress_city: diagaddress_city, - diagaddress_state: diagaddress_state, - diagaddress_zip: diagaddress_zip, - phonenumber: phonenumber, - ethnicity: find_extension(extensions, "http://hl7.org/fhir/us/core/StructureDefinition/us-core-ethnicity"), - occupation: find_extension(extensions), - race: find_extension(extensions, "http://hl7.org/fhir/us/core/StructureDefinition/us-core-race") - } + defp extract_resources(resources) do + result = Enum.reduce_while(resources, {:ok, %{}}, fn {resource_type, resource_list}, {:ok, acc} -> + case extract_resource(resource_type, resource_list) do + {:ok, extracted} -> {:cont, {:ok, Map.put(acc, resource_type, extracted)}} + {:error, reason} -> {:halt, {:error, reason}} + end end) + + case result do + {:ok, extracted} -> {:ok, extracted} + {:error, reason} -> {:error, reason} + end end - def extract_resource("Observation", resource_list) do - resource_list - |> Enum.map(fn - %EpiViewpoint.R4.Observation{ - id: lab_result_tid, - subject: %EpiViewpoint.R4.Reference{reference: "Patient/" <> pat_id}, - effective_date_time: datecollected, - issued: resultdate, - code: %EpiViewpoint.R4.CodeableConcept{text: testname}, - interpretation: [%EpiViewpoint.R4.CodeableConcept{coding: [%EpiViewpoint.R4.Coding{display: result}]}], - performer: [%EpiViewpoint.R4.Reference{reference: "Organization/" <> org_id}], - extension: extensions - } = _resource -> - %{ - lab_result_tid: lab_result_tid, - pat_id: pat_id, - datecollected: datecollected, - resultdate: resultdate |> DateTime.to_date() |> Calendar.strftime("%m/%d/%Y"), - testname: testname, - result: result, - org_id: org_id, - datereportedtolhd: find_extension(extensions) - } - end) + defp extract_resource("Patient", resource_list) do + {:ok, Enum.map(resource_list, &extract_patient/1)} end - def extract_resource("Organization", resource_list) do - resource_list - |> Enum.map(fn - %EpiViewpoint.R4.Organization{ - id: orgnization_id, - name: orderingfacilityname - } = _resource -> - %{ - orgnization_id: orgnization_id, - orderingfacilityname: orderingfacilityname - } - end) + defp extract_resource("Observation", resource_list) do + {:ok, Enum.map(resource_list, &extract_observation/1)} + end + + defp extract_resource("Organization", resource_list) do + {:ok, Enum.map(resource_list, &extract_organization/1)} + end + + defp extract_resource(unknown_type, _) do + {:error, "Unknown resource type: #{unknown_type}"} + end + + defp extract_patient(%EpiViewpoint.R4.Patient{ + id: caseid, + identifier: [%EpiViewpoint.R4.Identifier{value: person_tid}], + name: [%EpiViewpoint.R4.HumanName{given: [search_firstname], family: search_lastname}], + birth_date: dateofbirth, + gender: sex, + address: [ + %EpiViewpoint.R4.Address{ + line: [diagaddress_street1], + city: diagaddress_city, + state: diagaddress_state, + postal_code: diagaddress_zip + } + ], + telecom: [%EpiViewpoint.R4.ContactPoint{value: phonenumber}], + extension: extensions + }) do + %{ + caseid: caseid, + person_tid: person_tid, + search_firstname: search_firstname, + search_lastname: search_lastname, + dateofbirth: format_date(dateofbirth), + sex: String.capitalize(to_string(sex)), + diagaddress_street1: diagaddress_street1, + diagaddress_city: diagaddress_city, + diagaddress_state: diagaddress_state, + diagaddress_zip: diagaddress_zip, + phonenumber: phonenumber, + ethnicity: find_extension(extensions, "http://hl7.org/fhir/us/core/StructureDefinition/us-core-ethnicity"), + occupation: find_extension(extensions), + race: find_extension(extensions, "http://hl7.org/fhir/us/core/StructureDefinition/us-core-race") + } + end + + defp extract_observation(%EpiViewpoint.R4.Observation{ + id: lab_result_tid, + subject: %EpiViewpoint.R4.Reference{reference: "Patient/" <> pat_id}, + effective_date_time: datecollected, + issued: resultdate, + code: %EpiViewpoint.R4.CodeableConcept{text: testname}, + interpretation: [%EpiViewpoint.R4.CodeableConcept{coding: [%EpiViewpoint.R4.Coding{display: result}]}], + performer: [%EpiViewpoint.R4.Reference{reference: "Organization/" <> org_id}], + extension: extensions + }) do + %{ + lab_result_tid: lab_result_tid, + pat_id: pat_id, + datecollected: datecollected, + resultdate: format_date(resultdate), + testname: testname, + result: result, + org_id: org_id, + datereportedtolhd: find_extension(extensions) + } + end + + defp extract_organization(%EpiViewpoint.R4.Organization{ + id: organization_id, + name: ordering_facility_name + }) do + %{ + organization_id: organization_id, + ordering_facility_name: ordering_facility_name + } end - # Helper function to find extension values defp find_extension(extensions, url \\ nil) do Enum.find_value(extensions, fn %EpiViewpoint.R4.Extension{ @@ -136,29 +169,33 @@ defmodule EpiViewpoint.BulkFhirParser do end) end - defp json_to_kindle_schema(json, version_namespace) do - map = Jason.decode!(json) - Kindling.Converter.convert(version_namespace, map) - end - - def join_resources(resources) do + defp join_resources(resources) do patients = Map.get(resources, "Patient", []) observations = Map.get(resources, "Observation", []) organizations = Map.get(resources, "Organization", []) - observations + joined = observations |> Enum.map(fn observation -> patient = Enum.find(patients, &(&1.caseid == observation.pat_id)) - organization = Enum.find(organizations, &(&1.orgnization_id == observation.org_id)) + organization = Enum.find(organizations, &(&1.organization_id == observation.org_id)) observation |> Map.merge(patient || %{}) |> Map.merge(organization || %{}) - |> Map.drop([:pat_id, :org_id, :orgnization_id]) + |> Map.drop([:pat_id, :org_id, :organization_id]) end) + + {:ok, joined} end - def to_map(resources, contents) do + defp to_map(resources, contents) do %{file_name: "load.bulk_fhir", contents: contents, list: resources} end + + defp filter_empty_lines(line) do + line != "" + end + + defp format_date(date) when is_struct(date, Date), do: Calendar.strftime(date, "%m/%d/%Y") + defp format_date(datetime) when is_struct(datetime, DateTime), do: datetime |> DateTime.to_date() |> format_date() end diff --git a/lib/epiviewpoint/cases/import.ex b/lib/epiviewpoint/cases/import.ex index 7f667744..ee31ba47 100644 --- a/lib/epiviewpoint/cases/import.ex +++ b/lib/epiviewpoint/cases/import.ex @@ -61,9 +61,8 @@ defmodule EpiViewpoint.Cases.Import do end def import_bulk_fhir_data_file(lab_result_data_file_list, originator) do - lab_result_data_file_list - |> BulkFhirParser.parse_bulk_fhir() - |> import_data_file(originator) + {:ok, bulk_fhir_data} = BulkFhirParser.parse_bulk_fhir(lab_result_data_file_list) + import_data_file(bulk_fhir_data, originator) end def import_data_file(_file, %{admin: false}), do: {:error, "Originator must be an admin"} From 20cb94ad4d370da5e1dcffdbbe118d4cd42a29a7 Mon Sep 17 00:00:00 2001 From: Lei Zhou Date: Fri, 6 Sep 2024 18:29:11 -0400 Subject: [PATCH 4/8] manual browser test passed --- .../controllers/import_controller.ex | 6 ++- .../live/import_live.html.heex | 38 +++++++++++-------- 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/lib/epiviewpoint_web/controllers/import_controller.ex b/lib/epiviewpoint_web/controllers/import_controller.ex index 17fe7ca1..71d100d6 100644 --- a/lib/epiviewpoint_web/controllers/import_controller.ex +++ b/lib/epiviewpoint_web/controllers/import_controller.ex @@ -41,8 +41,12 @@ defmodule EpiViewpointWeb.ImportController do end end + def create_bulk_fhir(%{assigns: %{current_user: %{admin: false}}} = conn, _file) do + redirect(conn, to: "/") + end + def create_bulk_fhir(conn, %{"files" => plug_uploads}) do - result = UploadedFile.from_plug_uploads(plug_uploads) |> Cases.import_bulk_fhir_lab_results(conn.assigns.current_user) + result = plug_uploads |> UploadedFile.from_plug_uploads() |> Cases.import_bulk_fhir_lab_results(conn.assigns.current_user) case result do {:ok, import_info} -> diff --git a/lib/epiviewpoint_web/live/import_live.html.heex b/lib/epiviewpoint_web/live/import_live.html.heex index 70a02288..783949d6 100644 --- a/lib/epiviewpoint_web/live/import_live.html.heex +++ b/lib/epiviewpoint_web/live/import_live.html.heex @@ -6,21 +6,29 @@ <% end %>

- Choose a CSV or NDJSON file from your computer, click “Upload”, and then wait for the file to upload. + Choose a CSV or NDJSON file from your computer, click "Upload", and then wait for the file to upload.

- <%= form_tag("/import/upload", multipart: true, method: :post) %> + <%= form_tag("/import/upload", multipart: true, method: :post) do %> + + + <% end %> + +

- Choose bulk FHIR NDJSON files from your computer, click “Upload”, and then wait for the file to upload. + Choose bulk FHIR NDJSON files from your computer, click "Upload", and then wait for the file to upload.

- <%= form_tag("/import/upload_bulk_fhir", multipart: true, method: :post) %> -
+ <%= form_tag("/import/upload_bulk_fhir", multipart: true, method: :post) do %> + + + <% end %> + \ No newline at end of file From b86d94f9352ef67b169934b9cda16b3f2664c9bc Mon Sep 17 00:00:00 2001 From: Lei Zhou Date: Fri, 6 Sep 2024 18:53:04 -0400 Subject: [PATCH 5/8] code refactoring --- lib/epiviewpoint/bulk_fhir_parser.ex | 60 ++++++++++--------- .../live/import_live.html.heex | 20 ++----- 2 files changed, 37 insertions(+), 43 deletions(-) diff --git a/lib/epiviewpoint/bulk_fhir_parser.ex b/lib/epiviewpoint/bulk_fhir_parser.ex index e4641906..9aa0ba66 100644 --- a/lib/epiviewpoint/bulk_fhir_parser.ex +++ b/lib/epiviewpoint/bulk_fhir_parser.ex @@ -14,12 +14,13 @@ defmodule EpiViewpoint.BulkFhirParser do end defp load_resources(file_list) do - result = Enum.reduce_while(file_list, {:ok, %{}}, fn file, {:ok, acc} -> - case load_resource_file(file.contents) do - {:ok, resources} -> {:cont, {:ok, group_resources(resources, acc)}} - {:error, reason} -> {:halt, {:error, reason}} - end - end) + result = + Enum.reduce_while(file_list, {:ok, %{}}, fn file, {:ok, acc} -> + case load_resource_file(file.contents) do + {:ok, resources} -> {:cont, {:ok, group_resources(resources, acc)}} + {:error, reason} -> {:halt, {:error, reason}} + end + end) case result do {:ok, resources} -> {:ok, resources} @@ -34,12 +35,13 @@ defmodule EpiViewpoint.BulkFhirParser do end defp load_resource_file(file_content) do - result = file_content - |> String.split("\n") - |> Stream.map(&String.trim/1) - |> Stream.filter(&filter_empty_lines/1) - |> Stream.map(&json_to_kindle_schema/1) - |> Enum.to_list() + result = + file_content + |> String.split("\n") + |> Stream.map(&String.trim/1) + |> Stream.filter(&filter_empty_lines/1) + |> Stream.map(&json_to_kindle_schema/1) + |> Enum.to_list() {:ok, result} rescue @@ -54,12 +56,13 @@ defmodule EpiViewpoint.BulkFhirParser do end defp extract_resources(resources) do - result = Enum.reduce_while(resources, {:ok, %{}}, fn {resource_type, resource_list}, {:ok, acc} -> - case extract_resource(resource_type, resource_list) do - {:ok, extracted} -> {:cont, {:ok, Map.put(acc, resource_type, extracted)}} - {:error, reason} -> {:halt, {:error, reason}} - end - end) + result = + Enum.reduce_while(resources, {:ok, %{}}, fn {resource_type, resource_list}, {:ok, acc} -> + case extract_resource(resource_type, resource_list) do + {:ok, extracted} -> {:cont, {:ok, Map.put(acc, resource_type, extracted)}} + {:error, reason} -> {:halt, {:error, reason}} + end + end) case result do {:ok, extracted} -> {:ok, extracted} @@ -174,20 +177,21 @@ defmodule EpiViewpoint.BulkFhirParser do observations = Map.get(resources, "Observation", []) organizations = Map.get(resources, "Organization", []) - joined = observations - |> Enum.map(fn observation -> - patient = Enum.find(patients, &(&1.caseid == observation.pat_id)) - organization = Enum.find(organizations, &(&1.organization_id == observation.org_id)) - - observation - |> Map.merge(patient || %{}) - |> Map.merge(organization || %{}) - |> Map.drop([:pat_id, :org_id, :organization_id]) - end) + joined = Enum.map(observations, &merge_resources(&1, patients, organizations)) {:ok, joined} end + defp merge_resources(observation, patients, organizations) do + patient = Enum.find(patients, &(&1.caseid == observation.pat_id)) + organization = Enum.find(organizations, &(&1.organization_id == observation.org_id)) + + observation + |> Map.merge(patient || %{}) + |> Map.merge(organization || %{}) + |> Map.drop([:pat_id, :org_id, :organization_id]) + end + defp to_map(resources, contents) do %{file_name: "load.bulk_fhir", contents: contents, list: resources} end diff --git a/lib/epiviewpoint_web/live/import_live.html.heex b/lib/epiviewpoint_web/live/import_live.html.heex index 783949d6..7fbb09c5 100644 --- a/lib/epiviewpoint_web/live/import_live.html.heex +++ b/lib/epiviewpoint_web/live/import_live.html.heex @@ -10,25 +10,15 @@

<%= form_tag("/import/upload", multipart: true, method: :post) do %> - + <% end %> - -
+ +

Import Bulk FHIR

Choose bulk FHIR NDJSON files from your computer, click "Upload", and then wait for the file to upload.

<%= form_tag("/import/upload_bulk_fhir", multipart: true, method: :post) do %> - + <% end %> -
\ No newline at end of file + From f494a98aa3e464e46ba5f2b66793db7560aa06cb Mon Sep 17 00:00:00 2001 From: Lei Zhou Date: Fri, 6 Sep 2024 19:50:19 -0400 Subject: [PATCH 6/8] limit upload file type to ndjson --- lib/epiviewpoint_web/live/import_live.html.heex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/epiviewpoint_web/live/import_live.html.heex b/lib/epiviewpoint_web/live/import_live.html.heex index 7fbb09c5..d2774cee 100644 --- a/lib/epiviewpoint_web/live/import_live.html.heex +++ b/lib/epiviewpoint_web/live/import_live.html.heex @@ -18,7 +18,7 @@ Choose bulk FHIR NDJSON files from your computer, click "Upload", and then wait for the file to upload.

<%= form_tag("/import/upload_bulk_fhir", multipart: true, method: :post) do %> - + <% end %> From be670088e910f2b4233833a84ad98cd56543489b Mon Sep 17 00:00:00 2001 From: Lei Zhou Date: Mon, 9 Sep 2024 16:12:18 -0400 Subject: [PATCH 7/8] refactoring --- lib/epiviewpoint/bulk_fhir_parser.ex | 125 +++++++++++------- .../controllers/import_controller.ex | 4 +- test/epiviewpoint/data_file_test.exs | 40 +++--- 3 files changed, 99 insertions(+), 70 deletions(-) diff --git a/lib/epiviewpoint/bulk_fhir_parser.ex b/lib/epiviewpoint/bulk_fhir_parser.ex index 9aa0ba66..9ffa6cbe 100644 --- a/lib/epiviewpoint/bulk_fhir_parser.ex +++ b/lib/epiviewpoint/bulk_fhir_parser.ex @@ -39,7 +39,7 @@ defmodule EpiViewpoint.BulkFhirParser do file_content |> String.split("\n") |> Stream.map(&String.trim/1) - |> Stream.filter(&filter_empty_lines/1) + |> Stream.filter(&(&1 != "")) |> Stream.map(&json_to_kindle_schema/1) |> Enum.to_list() @@ -86,23 +86,25 @@ defmodule EpiViewpoint.BulkFhirParser do {:error, "Unknown resource type: #{unknown_type}"} end - defp extract_patient(%EpiViewpoint.R4.Patient{ - id: caseid, - identifier: [%EpiViewpoint.R4.Identifier{value: person_tid}], - name: [%EpiViewpoint.R4.HumanName{given: [search_firstname], family: search_lastname}], - birth_date: dateofbirth, - gender: sex, - address: [ - %EpiViewpoint.R4.Address{ - line: [diagaddress_street1], - city: diagaddress_city, - state: diagaddress_state, - postal_code: diagaddress_zip - } - ], - telecom: [%EpiViewpoint.R4.ContactPoint{value: phonenumber}], - extension: extensions - }) do + defp extract_patient(%EpiViewpoint.R4.Patient{} = patient) do + %EpiViewpoint.R4.Patient{ + id: caseid, + identifier: [%EpiViewpoint.R4.Identifier{value: person_tid}], + name: [%EpiViewpoint.R4.HumanName{given: [search_firstname], family: search_lastname}], + birth_date: dateofbirth, + gender: sex, + address: [ + %EpiViewpoint.R4.Address{ + line: [diagaddress_street1], + city: diagaddress_city, + state: diagaddress_state, + postal_code: diagaddress_zip + } + ], + telecom: [%EpiViewpoint.R4.ContactPoint{value: phonenumber}], + extension: extensions + } = patient + %{ caseid: caseid, person_tid: person_tid, @@ -115,22 +117,24 @@ defmodule EpiViewpoint.BulkFhirParser do diagaddress_state: diagaddress_state, diagaddress_zip: diagaddress_zip, phonenumber: phonenumber, - ethnicity: find_extension(extensions, "http://hl7.org/fhir/us/core/StructureDefinition/us-core-ethnicity"), - occupation: find_extension(extensions), - race: find_extension(extensions, "http://hl7.org/fhir/us/core/StructureDefinition/us-core-race") + ethnicity: find_extension(extensions, :ethnicity), + occupation: find_extension(extensions, :occupation), + race: find_extension(extensions, :race) } end - defp extract_observation(%EpiViewpoint.R4.Observation{ - id: lab_result_tid, - subject: %EpiViewpoint.R4.Reference{reference: "Patient/" <> pat_id}, - effective_date_time: datecollected, - issued: resultdate, - code: %EpiViewpoint.R4.CodeableConcept{text: testname}, - interpretation: [%EpiViewpoint.R4.CodeableConcept{coding: [%EpiViewpoint.R4.Coding{display: result}]}], - performer: [%EpiViewpoint.R4.Reference{reference: "Organization/" <> org_id}], - extension: extensions - }) do + defp extract_observation(%EpiViewpoint.R4.Observation{} = observation) do + %EpiViewpoint.R4.Observation{ + id: lab_result_tid, + subject: %EpiViewpoint.R4.Reference{reference: "Patient/" <> pat_id}, + effective_date_time: datecollected, + issued: resultdate, + code: %EpiViewpoint.R4.CodeableConcept{text: testname}, + interpretation: [%EpiViewpoint.R4.CodeableConcept{coding: [%EpiViewpoint.R4.Coding{display: result}]}], + performer: [%EpiViewpoint.R4.Reference{reference: "Organization/" <> org_id}], + extension: extensions + } = observation + %{ lab_result_tid: lab_result_tid, pat_id: pat_id, @@ -139,31 +143,60 @@ defmodule EpiViewpoint.BulkFhirParser do testname: testname, result: result, org_id: org_id, - datereportedtolhd: find_extension(extensions) + datereportedtolhd: find_extension(extensions, :datereportedtolhd) } end - defp extract_organization(%EpiViewpoint.R4.Organization{ - id: organization_id, - name: ordering_facility_name - }) do + defp extract_organization(%EpiViewpoint.R4.Organization{} = organization) do + %EpiViewpoint.R4.Organization{ + id: organization_id, + name: ordering_facility_name + } = organization + %{ organization_id: organization_id, ordering_facility_name: ordering_facility_name } end - defp find_extension(extensions, url \\ nil) do - Enum.find_value(extensions, fn + defp find_extension(extensions, :race) do + Enum.find_value(extensions, nil, fn + %EpiViewpoint.R4.Extension{ + url: "http://hl7.org/fhir/us/core/StructureDefinition/us-core-race", + extension: [%EpiViewpoint.R4.Extension{url: "ombCategory", value_coding: %EpiViewpoint.R4.Coding{display: value}}, _] + } -> + value + + _ -> + nil + end) + end + + defp find_extension(extensions, :ethnicity) do + Enum.find_value(extensions, nil, fn %EpiViewpoint.R4.Extension{ - url: ^url, + url: "http://hl7.org/fhir/us/core/StructureDefinition/us-core-ethnicity", extension: [%EpiViewpoint.R4.Extension{url: "ombCategory", value_coding: %EpiViewpoint.R4.Coding{display: value}}, _] } -> value + _ -> + nil + end) + end + + defp find_extension(extensions, :occupation) do + Enum.find_value(extensions, nil, fn %EpiViewpoint.R4.Extension{url: "http://hl7.org/fhir/StructureDefinition/patient-occupation", value_string: value} -> value + _ -> + nil + end) + end + + defp find_extension(extensions, :datereportedtolhd) do + Enum.find_value(extensions, nil, fn %EpiViewpoint.R4.Extension{url: "http://hl7.org/fhir/StructureDefinition/datereportedtolhd", value_date: value} -> value @@ -183,12 +216,12 @@ defmodule EpiViewpoint.BulkFhirParser do end defp merge_resources(observation, patients, organizations) do - patient = Enum.find(patients, &(&1.caseid == observation.pat_id)) - organization = Enum.find(organizations, &(&1.organization_id == observation.org_id)) + patient = Enum.find(patients, %{}, &(&1.caseid == observation.pat_id)) + organization = Enum.find(organizations, %{}, &(&1.organization_id == observation.org_id)) observation - |> Map.merge(patient || %{}) - |> Map.merge(organization || %{}) + |> Map.merge(patient) + |> Map.merge(organization) |> Map.drop([:pat_id, :org_id, :organization_id]) end @@ -196,10 +229,6 @@ defmodule EpiViewpoint.BulkFhirParser do %{file_name: "load.bulk_fhir", contents: contents, list: resources} end - defp filter_empty_lines(line) do - line != "" - end - defp format_date(date) when is_struct(date, Date), do: Calendar.strftime(date, "%m/%d/%Y") - defp format_date(datetime) when is_struct(datetime, DateTime), do: datetime |> DateTime.to_date() |> format_date() + defp format_date(datetime) when is_struct(datetime, DateTime), do: DateTime.to_date(datetime) |> format_date() end diff --git a/lib/epiviewpoint_web/controllers/import_controller.ex b/lib/epiviewpoint_web/controllers/import_controller.ex index 71d100d6..9c250747 100644 --- a/lib/epiviewpoint_web/controllers/import_controller.ex +++ b/lib/epiviewpoint_web/controllers/import_controller.ex @@ -23,7 +23,7 @@ defmodule EpiViewpointWeb.ImportController do case result do {:ok, import_info} -> - {_imported_people, popped_import_info} = import_info |> Map.pop(:imported_people) + {_imported_people, popped_import_info} = Map.pop(import_info, :imported_people) conn |> Session.set_last_file_import_info(popped_import_info) @@ -50,7 +50,7 @@ defmodule EpiViewpointWeb.ImportController do case result do {:ok, import_info} -> - {_imported_people, popped_import_info} = import_info |> Map.pop(:imported_people) + {_imported_people, popped_import_info} = Map.pop(import_info, :imported_people) conn |> Session.set_last_file_import_info(popped_import_info) diff --git a/test/epiviewpoint/data_file_test.exs b/test/epiviewpoint/data_file_test.exs index 53bd540a..a07f13aa 100644 --- a/test/epiviewpoint/data_file_test.exs +++ b/test/epiviewpoint/data_file_test.exs @@ -218,11 +218,11 @@ defmodule EpiViewpoint.DataFileTest do } ] - assert {:ok, result} = - DataFile.read(input, :bulk_fhir, &Function.identity/1, - required: ~w{first_name last_name dob sample_date result_date result}, - optional: ~w{} - ) + {:ok, result} = + DataFile.read(input, :bulk_fhir, &Function.identity/1, + required: ~w{first_name last_name dob sample_date result_date result}, + optional: ~w{} + ) assert result == [ %{ @@ -253,11 +253,11 @@ defmodule EpiViewpoint.DataFileTest do } ] - assert {:ok, result} = - DataFile.read(input, :bulk_fhir, &Function.identity/1, - required: ~w{column_a}, - optional: ~w{column_b} - ) + {:ok, result} = + DataFile.read(input, :bulk_fhir, &Function.identity/1, + required: ~w{column_a}, + optional: ~w{column_b} + ) assert result == [%{"column_a" => "value_a", "column_b" => "value_b"}] end @@ -288,11 +288,11 @@ defmodule EpiViewpoint.DataFileTest do } ] - assert {:ok, result} = - DataFile.read(input, :bulk_fhir, &Function.identity/1, - required: ~w{column_a column_b}, - optional: ~w{optional_c optional_d} - ) + {:ok, result} = + DataFile.read(input, :bulk_fhir, &Function.identity/1, + required: ~w{column_a column_b}, + optional: ~w{optional_c optional_d} + ) assert result == [%{"column_a" => "value_a", "column_b" => "value_b", "optional_c" => "value_c"}] end @@ -309,11 +309,11 @@ defmodule EpiViewpoint.DataFileTest do } ] - assert {:ok, result} = - DataFile.read(input, :bulk_fhir, fn headers -> Enum.map(headers, &String.upcase/1) end, - required: ~w{FIRST_NAME LAST_NAME}, - optional: ~w{} - ) + {:ok, result} = + DataFile.read(input, :bulk_fhir, fn headers -> Enum.map(headers, &String.upcase/1) end, + required: ~w{FIRST_NAME LAST_NAME}, + optional: ~w{} + ) assert result == [ %{ From 49426d7bf6ced1036d04c643dc8a9341896d607d Mon Sep 17 00:00:00 2001 From: Lei Zhou Date: Tue, 10 Sep 2024 15:23:34 -0400 Subject: [PATCH 8/8] minor refactoring --- lib/epiviewpoint/bulk_fhir_parser.ex | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/lib/epiviewpoint/bulk_fhir_parser.ex b/lib/epiviewpoint/bulk_fhir_parser.ex index 9ffa6cbe..834e0a42 100644 --- a/lib/epiviewpoint/bulk_fhir_parser.ex +++ b/lib/epiviewpoint/bulk_fhir_parser.ex @@ -148,14 +148,9 @@ defmodule EpiViewpoint.BulkFhirParser do end defp extract_organization(%EpiViewpoint.R4.Organization{} = organization) do - %EpiViewpoint.R4.Organization{ - id: organization_id, - name: ordering_facility_name - } = organization - %{ - organization_id: organization_id, - ordering_facility_name: ordering_facility_name + organization_id: organization.id, + ordering_facility_name: organization.name } end