diff --git a/.fern/metadata.json b/.fern/metadata.json
index 9c28ff2..d2baeb2 100644
--- a/.fern/metadata.json
+++ b/.fern/metadata.json
@@ -1,7 +1,7 @@
{
- "cliVersion": "5.44.4",
+ "cliVersion": "5.95.1",
"generatorName": "fernapi/fern-python-sdk",
- "generatorVersion": "5.12.1",
+ "generatorVersion": "5.27.1",
"generatorConfig": {
"package_name": "junction",
"client_class_name": "Junction",
@@ -10,10 +10,10 @@
"use_pydantic_field_aliases": true
}
},
- "originGitCommit": "5a5c949d3b80b9f7527c210537683fed5e91dfa6",
+ "originGitCommit": "1f6b39f773631700784a588e04910b954fbe1383",
"originGitCommitIsDirty": true,
"invokedBy": "ci",
"requestedVersion": "AUTO",
"ciProvider": "unknown",
- "sdkVersion": "1.3.0"
+ "sdkVersion": "1.4.0"
}
\ No newline at end of file
diff --git a/.fern/replay.lock b/.fern/replay.lock
index 2ee9a1f..cce9c58 100644
--- a/.fern/replay.lock
+++ b/.fern/replay.lock
@@ -6,5 +6,11 @@ generations:
timestamp: 2026-06-03T14:06:31.122Z
cli_version: unknown
generator_versions: {}
-current_generation: 45892c3cbfff6c98aa0820e3299d58a6a93c4f97
+ - commit_sha: 8101028c0b1297ad90eacddc9226d7617d7e42c9
+ tree_hash: a4b3639f107d7e44592ed8f73907fc9d5f7187e1
+ timestamp: 2026-08-14T07:32:42.449Z
+ cli_version: unknown
+ generator_versions:
+ fernapi/fern-python-sdk: 5.27.1
+current_generation: 8101028c0b1297ad90eacddc9226d7617d7e42c9
patches: []
diff --git a/README.md b/README.md
index 3a2f0b5..283b447 100644
--- a/README.md
+++ b/README.md
@@ -178,7 +178,7 @@ client = Junction(..., timeout=20.0)
# Override timeout for a specific method
client.link.bulk_import(..., request_options={
- "timeout_in_seconds": 1
+ "timeout": 1
})
```
diff --git a/changelog.md b/changelog.md
index e716f10..aa55b9d 100644
--- a/changelog.md
+++ b/changelog.md
@@ -1,3 +1,21 @@
+## 1.4.0 - 2026-08-14
+
+### Added
+
+* **Orderable-test search** — added sync and async `CompendiumClient.search_orderable_tests()` methods and the related request and response models.
+* **Unmatched lab-result management** — added sync and async methods for listing, testing, reviewing, accepting, and resolving unmatched results, together with match-review webhook models.
+* **Lab-test pricing** — added pricing models and optional `include_pricing` and `lab_account_id` request fields.
+* **Provider and lab coverage** — added Google Health provider and OAuth values and the MTL lab value.
+* **Lab metadata** — added optional source interpretation, lab logo URL, and lab-location website fields.
+
+### Changed
+
+* **SSE reliability** — resumable event streams now reconnect transparently, with client- and request-level controls for enabling reconnection and limiting attempts.
+
+### Beta
+
+* **Aggregate and lab-report states** — added the result-table resource and processing-error parsing state without affecting the stable-surface SemVer calculation.
+
## 1.3.0 - 2026-06-05
### Added
* **`AlignExpr`** — new public symbol
diff --git a/poetry.lock b/poetry.lock
index e069f9e..14a0dbb 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -1,142 +1,146 @@
-# This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand.
+# This file is automatically @generated by Poetry 2.4.1 and should not be changed by hand.
[[package]]
name = "aiohappyeyeballs"
-version = "2.6.2"
+version = "2.7.1"
description = "Happy Eyeballs for asyncio"
optional = true
python-versions = ">=3.10"
+groups = ["main"]
+markers = "extra == \"aiohttp\""
files = [
- {file = "aiohappyeyeballs-2.6.2-py3-none-any.whl", hash = "sha256:4708045e2d7a6c6bdf8aafa8ed39649eaf926a4543b54560659129e3365953c4"},
- {file = "aiohappyeyeballs-2.6.2.tar.gz", hash = "sha256:e202810ee718bd01fc6ef49e8ea53d023d5cb6b581076d7925aa499fa55dbe64"},
+ {file = "aiohappyeyeballs-2.7.1-py3-none-any.whl", hash = "sha256:9243213661e29250eb41368e5daa826fc017156c3b8a11440826b2e3ed376472"},
+ {file = "aiohappyeyeballs-2.7.1.tar.gz", hash = "sha256:065665c041c42a5938ed220bdcd7230f22527fbec085e1853d2402c8a3615d9d"},
]
[[package]]
name = "aiohttp"
-version = "3.14.0"
+version = "3.14.3"
description = "Async http client/server framework (asyncio)"
optional = true
python-versions = ">=3.10"
+groups = ["main"]
+markers = "extra == \"aiohttp\""
files = [
- {file = "aiohttp-3.14.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:692e409052e7436029bbb32977cd7c5bf806ac5fa4085b973996785ffadad33c"},
- {file = "aiohttp-3.14.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:40af7ebe53c7990e110dc4ad03566b12c3ac996254298a3d39046dd69cfcb2c2"},
- {file = "aiohttp-3.14.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02cb2ffbb7da32f82e21ad9952669c45bd88a80e0878264c2f59fe1c6fb2badd"},
- {file = "aiohttp-3.14.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2e2514cb7195f6d7c219339635bea71ae47d1569b051300d32df9dcfabcdb869"},
- {file = "aiohttp-3.14.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:30e8b7eeb42d02c120ca90d6c6e076a221a16b70a6dac9ae44c7ab5104cc7fe4"},
- {file = "aiohttp-3.14.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:63e38be0d75a654deaa06be32fb4cab883a4222940be1d05861b6717679cbadb"},
- {file = "aiohttp-3.14.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1210d4c87cc00128160c7384ab41877a701295b97cffa6362f908a49b6e8a7ca"},
- {file = "aiohttp-3.14.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1a78a77366ed158a0a54b076990e575d7b7cdb728cbfd02711eadab150f2269f"},
- {file = "aiohttp-3.14.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f4d2038c64f36df96cfd3fa0937910e231eafbf897e70a06c155a817bb632fa6"},
- {file = "aiohttp-3.14.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4714c70067a08b604d0bf3bc4dfdf82e52944afab41d0428d460862763d2f79b"},
- {file = "aiohttp-3.14.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:f79bfd2847513a7ac801bbafd1de02348a37926ac439eeb4bfe96fcff4eada15"},
- {file = "aiohttp-3.14.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:25e9f1d2465a210d60edb64d7b204a147e85d4c194eecef3d1604fb5ace678ce"},
- {file = "aiohttp-3.14.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:b5314743ebe926c2fda35d0a298c565c885505f6635c2a30936363404cf274a7"},
- {file = "aiohttp-3.14.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:28eee8de1d69711c53116df8202f1c2aa0e3f80ef912a88fc18d159d53e7110b"},
- {file = "aiohttp-3.14.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:89ed35666c95d3efe1955056afcde09e62a57a34e2a4398b17f9f6c1564f0b25"},
- {file = "aiohttp-3.14.0-cp310-cp310-win32.whl", hash = "sha256:5e4646e9a6af29af354204011bf5769cb0276ec5b64653e42f90b3e13845169f"},
- {file = "aiohttp-3.14.0-cp310-cp310-win_amd64.whl", hash = "sha256:22a8d06f204e0518a586d770032db3c7043c9ba3693081b3e3ad425e1458d594"},
- {file = "aiohttp-3.14.0-cp310-cp310-win_arm64.whl", hash = "sha256:4acfc34bd4d3c58754fc9f22ff1b5e92aabce68f3d4bf7b71a0b732d9bceb78a"},
- {file = "aiohttp-3.14.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:54bf3522d6f7351e55f89a62d5c2bf138ad557b031670266c5df604ae88e0b5a"},
- {file = "aiohttp-3.14.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0746d9fb0ac4fdef643a84494efe3f06d50335dd8c7a530228b86448aae0a803"},
- {file = "aiohttp-3.14.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9f3a96b6d39a4872222beee72e1df41d2ff886ae96152cf3e757ef8c5673ef0e"},
- {file = "aiohttp-3.14.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d336820adbb914debbc90a1d8c1bfc4bea55996aecf64866a989d35d1f9fd903"},
- {file = "aiohttp-3.14.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:71b2604c9bfc1b115547d63a094d5244b3f02799833513a99a68aaa7b167c4cb"},
- {file = "aiohttp-3.14.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:610d68800435903e303ca0542b9d3e4eb72a12ff33a6d471a070c1d81eebd3c2"},
- {file = "aiohttp-3.14.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:514db9a79337068981ee2137310283a07b4b885c584991097a91a4da419bcb81"},
- {file = "aiohttp-3.14.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c452d17eeb95d563fc8b936f3050301dbd1d268126c4632d8b70ede9696202ee"},
- {file = "aiohttp-3.14.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ed94a81506e3d1bdbad5108f497a58f2a2354aedb4ca314d5326f07d1fd1ac2d"},
- {file = "aiohttp-3.14.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1394dce36e0f0d260ac0b555a654de19cb989f3c1b8bdd24f505314dfea18a00"},
- {file = "aiohttp-3.14.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:d1467d1e7b48a73ca7237e0ee4335f3d02b923dbc27b82fd254bc301c97d4026"},
- {file = "aiohttp-3.14.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:6a5f3532125233c261cf61f32df4059cfcf482eb793c7d3db8452e3142028b86"},
- {file = "aiohttp-3.14.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:3ea81eb518a2ecb319d8ec6d1424a37c773f6634bd87d6985eb606b2faac419f"},
- {file = "aiohttp-3.14.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:32e735c3182de7b64f6941a4ede48b38c7f47d9437bd615dd30b5bda8fa1bc93"},
- {file = "aiohttp-3.14.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c21ca9a1c63d4509158f478aeb9d02914dcc52adc68d1bc9dee2452284ee5996"},
- {file = "aiohttp-3.14.0-cp311-cp311-win32.whl", hash = "sha256:19ca5fc84130675ba11c6ca5c7da5cb65f7bf8a32cdd2b616bf49cd334688aae"},
- {file = "aiohttp-3.14.0-cp311-cp311-win_amd64.whl", hash = "sha256:d488e6e9d3bb8ba5ae7066d5be885ae9670eba021b8c6ccb9a3a568e6b19d6e5"},
- {file = "aiohttp-3.14.0-cp311-cp311-win_arm64.whl", hash = "sha256:8b93618102caf12801638a01a2b478a55410ddd71bd41cfaf6f707953a49ac43"},
- {file = "aiohttp-3.14.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b29518c9c2ec7e373e68259206a137c7f4f5439c58baaec4b5ab3ab799850a4e"},
- {file = "aiohttp-3.14.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:dbec68ce61b64cb73cab4d33df9433427b1713c8bcccb181dce695c1b6f8e87c"},
- {file = "aiohttp-3.14.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3cdf534aa455593e589302990c5097aa5c92c06c4262a20da22934f9186a5fff"},
- {file = "aiohttp-3.14.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cb6c657104393b5fbff01a5f59b2023db74058a8077d94475d6c25d03882a108"},
- {file = "aiohttp-3.14.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:46fbbec4e4fab7428d4396a3823f9320e4560aa3113b89eeebce712c27c9ed5a"},
- {file = "aiohttp-3.14.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2c2c7e05dd5335b298085abf45ddf98673934c3ee1c083d0b9ea13d4186ad500"},
- {file = "aiohttp-3.14.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3c7139100fbaae76515b73051d8f0aa3a3ff02e415eec8a8eee8e2223d9ba955"},
- {file = "aiohttp-3.14.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:78d6f9286a629ce52728430afe18f8ed2b6c39a1fddb3802d7244b9983910ad2"},
- {file = "aiohttp-3.14.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cc3c3e12cdaeb92d7dcf13db00e9f6b1956b910e47256e696df1cfa946d02159"},
- {file = "aiohttp-3.14.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4d6a998191f5ebe3b8c28463ff72bc030250008b3193c402464efadd08b5ca02"},
- {file = "aiohttp-3.14.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:0fc2b75ae8d169d853be2862d960be8550da6c5c65711d5476407eb3fdb006bd"},
- {file = "aiohttp-3.14.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:16eee56bcc72d04600bc56c1759982c2385ec0b41d3fd3521f836bf64a0957ef"},
- {file = "aiohttp-3.14.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:5a2e7ca615c3ddc15b82687e05a624e5f5cba3f1d6c20cb81172d70ea498451e"},
- {file = "aiohttp-3.14.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:f0b7b8bbbec3ce9467ee0ebe334622fd90624f593edd3136c567811453fc4fae"},
- {file = "aiohttp-3.14.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5ba10966d4f03dd96a14365be4b8e37c327c76f11c3ca867116966cdd9f98066"},
- {file = "aiohttp-3.14.0-cp312-cp312-win32.whl", hash = "sha256:101df7779c80c0636014a6b2c6642acd3efb5b355d48347c9d7dfb720aee9430"},
- {file = "aiohttp-3.14.0-cp312-cp312-win_amd64.whl", hash = "sha256:b0a5747586d4467efd1f932710b269131c9717a872dce082cd92a00c1c13123a"},
- {file = "aiohttp-3.14.0-cp312-cp312-win_arm64.whl", hash = "sha256:5f1c5be60add78fabb4aacd13c5a348ae79d2fcbfc7fa78da8f1eb192273b370"},
- {file = "aiohttp-3.14.0-cp313-cp313-android_21_arm64_v8a.whl", hash = "sha256:25400d710641a8040bf022a8a99f579e581ffa1c5bd42c33255d7d6f3957c127"},
- {file = "aiohttp-3.14.0-cp313-cp313-android_21_x86_64.whl", hash = "sha256:c5492b9929826e07cc3fcb9739ae87aab05dff6b5e67a9b73fd1700c6d008981"},
- {file = "aiohttp-3.14.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:3366751d68d237c621264233a32f3078bbc21b7904ab90a77e03d21390c742c6"},
- {file = "aiohttp-3.14.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:57ea07d28695a7a40304d42251892a8df765e5588c10ee32afeddcd5df33c0a2"},
- {file = "aiohttp-3.14.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:076cb014191ae2e65d949e1ad01f1dcfe33e32789b5172510f3e79c79fc04d50"},
- {file = "aiohttp-3.14.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2f3fc37054564dee64a855b5b092d87ec35dcddfaabf7dacb1c8a2b1f83dc0a9"},
- {file = "aiohttp-3.14.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8fcaef74d2ab0f607d7ff85a0d15e21bb5a258c4a58df1908396eb50d7f4ed3c"},
- {file = "aiohttp-3.14.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e4c01b0bfc6209590960e68eac083cd22d5d87c21f974dd6208cafa5d3542bc8"},
- {file = "aiohttp-3.14.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f12eb7896e81caf403a2b18c9406426f1207361e7239c057ab29c076d4257e83"},
- {file = "aiohttp-3.14.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6c79a044cacf360ec46738d863d2f41c9300d2a06ef4a7402ea0df306a350e61"},
- {file = "aiohttp-3.14.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:85e0675f47be4eff0636bf88c02140ea89168ae0df3ff1f3f464e9de9610d277"},
- {file = "aiohttp-3.14.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7b33e751cab03fdc960095b1e326cb5a03f5ee577d6ded59f3d1c100f8668882"},
- {file = "aiohttp-3.14.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:26d9224c6dd7f5c749aba4f61315a894601448b28d94d12f4dea0903e26d2096"},
- {file = "aiohttp-3.14.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6281aecdf2732940f4fe06bd6adec5ae4d59b78b080b8e3a6b81467301010988"},
- {file = "aiohttp-3.14.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:23e8314e7aed8576fbe33314d218bd81447a3adbc91dc36f1163bf583cd3084c"},
- {file = "aiohttp-3.14.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:3b54fbff46127aeafdd764cecd0d99fa2f24a0e37ea5c18a7c3a4ac450df1db3"},
- {file = "aiohttp-3.14.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b27d89af91a555f58e08e4902dbcbc48862fd40095720ca705990476bd93b7ac"},
- {file = "aiohttp-3.14.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:25d2326a4967bf705a9f9913a13005e93b6020ad8a9f6bd6bd78850d5171332e"},
- {file = "aiohttp-3.14.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:a1d209375c503472b3c0a340cdf3c55fcd82e84b46dda7caeaced59faba373ec"},
- {file = "aiohttp-3.14.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:666c7c5036df57b693026398b69b41874a1931ac5b3485fd910e57bfac253869"},
- {file = "aiohttp-3.14.0-cp313-cp313-win32.whl", hash = "sha256:23f094a1ef64823fd35854ddf5c7a80a078162f37f9d2f7c6142b51a6affa456"},
- {file = "aiohttp-3.14.0-cp313-cp313-win_amd64.whl", hash = "sha256:e03abdaa17d553f17e1d1d06bb266b3970106c78051d06795723e748d8e49d11"},
- {file = "aiohttp-3.14.0-cp313-cp313-win_arm64.whl", hash = "sha256:acdb400538cf4769543548bb5d1eb23d39bed4f96554a6078cb728c7cb2c268b"},
- {file = "aiohttp-3.14.0-cp314-cp314-android_24_arm64_v8a.whl", hash = "sha256:363ef9e91014e7891679bfb2ac0a7c6ea93435dbbfd10ecf41b9f06fcf506c5f"},
- {file = "aiohttp-3.14.0-cp314-cp314-android_24_x86_64.whl", hash = "sha256:884a4edbdad77be9d0ef36142c8b504351b170df0bf62b51e784fadabf311c42"},
- {file = "aiohttp-3.14.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:70ea956f6cc4a37620966b56c2e205d88ca3e6d85ec063277e414b1035cddad3"},
- {file = "aiohttp-3.14.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:ea3b9806c89f61da22fddf1f12dd524fb368e5e28f1261fbdafe5c3cd8ce893b"},
- {file = "aiohttp-3.14.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:a071be341c2bd9b0188e62d173509f024e0a35b1c342c53c50f8daaeda8c3bd8"},
- {file = "aiohttp-3.14.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:198cfe61bf253b19da1fb3e0fa122249dc4f14c12709493fed8054aa0411cc76"},
- {file = "aiohttp-3.14.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:9dc203d6ce6b9106d54e2a93f41dfdfebfbca2d99962ba503bfd3e5921a6549e"},
- {file = "aiohttp-3.14.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9e19d17ab02bf16832a2c8c0d55a486792c5b1645665652ee9531aebcc30cb72"},
- {file = "aiohttp-3.14.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d925fba0c14d5b498a8028b0107beebdfd16c5d48d702ff54f879cb017aaaca3"},
- {file = "aiohttp-3.14.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d33e61021222ce7f9792bcac870d6f58d8adfceda33ab857b01264f4560f2c5f"},
- {file = "aiohttp-3.14.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:44eca38755d0105bb32f47d085f5dd449846a449e1245fc105889e3279dcf8e3"},
- {file = "aiohttp-3.14.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f13087e06f68fea4941c21a0c541c00553aa16e4f8fd7bbe2b198df761e964d6"},
- {file = "aiohttp-3.14.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ff82be7f1ef73634cb77890a770743239bc3d487b848669be1c599889336dc0a"},
- {file = "aiohttp-3.14.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a150c0875ac8fd87f1c398650841308a30d65facf7416b12dbdb9cfdcbe5a48c"},
- {file = "aiohttp-3.14.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:edc01ea4e1ec5a1649a28866262bf24195889ff7b27bdd947029a6086741de9b"},
- {file = "aiohttp-3.14.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:540632bf882ff8fc88f2e1697be0761578e89e0d79fb4a8a6d65dc5da7e729d4"},
- {file = "aiohttp-3.14.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:860a86bc2c80237f5dff52edcf427e10a8d8352271fd84845429a3e60199e02c"},
- {file = "aiohttp-3.14.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:5cbd50e6a50d6b99283a826b18cbdebf65b0797689a7535cb0e9dd37be0f63c3"},
- {file = "aiohttp-3.14.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:20144819e99db593e22bbd2f3f2691a5e149f879142d6b8670254708853ff4fb"},
- {file = "aiohttp-3.14.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:26b6d79aa54cb4ed50cc7d41ed14e99e0f1fc8e7c2d42f2e05b37aea897b2b52"},
- {file = "aiohttp-3.14.0-cp314-cp314-win32.whl", hash = "sha256:106ed074a856f3e21d186b8579e2c8afb6da598e267cdaab01059e13db2fc44d"},
- {file = "aiohttp-3.14.0-cp314-cp314-win_amd64.whl", hash = "sha256:4f770846edae8f00ecc57af825bce811f787f87a7dcf0e90d191790efe5b31f7"},
- {file = "aiohttp-3.14.0-cp314-cp314-win_arm64.whl", hash = "sha256:acf1581c4f21ed4b80a2dded504d87b055a071a84d5737ea966435f768275ac6"},
- {file = "aiohttp-3.14.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:6aa1a40f9cbb3da9f80714c5966b8946c21e6a2530d809b9498b33161e3c8733"},
- {file = "aiohttp-3.14.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:b62af5a8cc96a194eaa01a9ed7b34a3ffa58d3d8daaa1a0d7a749353ad12d228"},
- {file = "aiohttp-3.14.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6eb63b1417efaf7d1002a6ad034a40d44376afcc16508a57f8e74b49ad26a095"},
- {file = "aiohttp-3.14.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c20b9ad156a79eb97be5cf9e069eec01d2f0dc8472ffbd75299a8b2d4c2cbbde"},
- {file = "aiohttp-3.14.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:40ae7b0642c25632c7eabc4a04754012691864d2a1b93becf7cddb76027b838a"},
- {file = "aiohttp-3.14.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:95f5217e76a046b9f228a101717ef8d42b1eb3d9d196d15202db5bf41df88936"},
- {file = "aiohttp-3.14.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1a4a9f17e85b80878c176695c1998c790e83731d8271881e5d356488652a1f9e"},
- {file = "aiohttp-3.14.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:145262119b07d7f95abc1839add35ba2bfc84551d4b4660ca11542c0b215455b"},
- {file = "aiohttp-3.14.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:49a33ded29b0b2fa7a367a02cf0fb89af602bb87542a16177ec8ce1c9c51d12a"},
- {file = "aiohttp-3.14.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:2cc736a9c9fc2bc4dd71fd404815741b6573df27c3f985948ec4076989ac57de"},
- {file = "aiohttp-3.14.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:b4141a3e5342ee3053a9cab54d25b64ed28289c1041e4c54b3d99839314d90ce"},
- {file = "aiohttp-3.14.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:e30871b2d58996cb81aac52d2b1d15ac05257131ef0f90f18c2115a380fbfe7c"},
- {file = "aiohttp-3.14.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:667b881d083ccae3900ea5a241e17e5007ca78844c53ed389bb63d48f729d9c7"},
- {file = "aiohttp-3.14.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:b584dfe615d151e9b8f0a8ecb3aee6147f2927ec5b95ba25fe621f5377510928"},
- {file = "aiohttp-3.14.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6199707cc40e0e9cd39c36fbc97bec416c704e1d0ddce03412bb3b3e6a90ccd0"},
- {file = "aiohttp-3.14.0-cp314-cp314t-win32.whl", hash = "sha256:a8d93334d4961c9d566b1f046c81dee475b7c21eb730728d38237bfa70d1c8e6"},
- {file = "aiohttp-3.14.0-cp314-cp314t-win_amd64.whl", hash = "sha256:2d2ffe9b614f50f069068b3b52e73414e4107fc10b7efc939a76acff9251fdd2"},
- {file = "aiohttp-3.14.0-cp314-cp314t-win_arm64.whl", hash = "sha256:7a3fc4358e65826c515350f199c210de747cf669998211b1ee6c2e46de364b24"},
- {file = "aiohttp-3.14.0.tar.gz", hash = "sha256:2882de819734c715fd1b9c11c97e09fa020d14438203d1d354d8ed1702791c9b"},
+ {file = "aiohttp-3.14.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:eb0495d778817619273c108784292be161a924b9f5ae5cbbc70a2caa6838250b"},
+ {file = "aiohttp-3.14.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c3c200cf9757edd785051dc699c7ecbec22110dbfcb3fefc7a9f9695eda8ea7a"},
+ {file = "aiohttp-3.14.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd51ebf9d3a00c074df4ede271023f4d2dba289bcc740b88191872716014e3c5"},
+ {file = "aiohttp-3.14.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:134ac5ddcf61c6fad984b9a5727d83492ada43d63471db20fb73042c13fca62f"},
+ {file = "aiohttp-3.14.3-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:70c987b27534f9ae1a723f47ae921571d616da21d3208282bf4c52af5164ac43"},
+ {file = "aiohttp-3.14.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1b59533861b70a2185c8f4f350f791f39d64358ef6944ce71c5240c9ec0982c9"},
+ {file = "aiohttp-3.14.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1c5281acc88b92396f88c7e1e2748f8466689df22b80170e4f51efa712fb47a8"},
+ {file = "aiohttp-3.14.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:48d67b87db6279c044760787eb01f6413032c2e6f3ba1cafaa492b1c8e578479"},
+ {file = "aiohttp-3.14.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f53bcd52f585e1ac3e590d61434eb61f9a88c38df041b4ea126d97144344a77b"},
+ {file = "aiohttp-3.14.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0fdea2281997af69da84c77ffa6f5938a0285f21fb3887c249d67419ca865b3d"},
+ {file = "aiohttp-3.14.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:cda5fd5c95ad7a125a2e8464acc78b98b94c475a3780d6aa0aa157c93f470f4d"},
+ {file = "aiohttp-3.14.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:6debfa7312ff9d4c124dc71d72e9a0a4b9e0879e48ba6fcb42bef5c3300289e2"},
+ {file = "aiohttp-3.14.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:f4e05329faa0ea1a404b37de4f034fd2c2defcca06a68dc6745e4e56c88e8a48"},
+ {file = "aiohttp-3.14.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:a3a8296e7ab5c295f53f1041487cb088e1480775aafbf7fe545d93b770a0f96f"},
+ {file = "aiohttp-3.14.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5373dc80ad1aa2fb9ad95c83f24eef418bbda3a61375f128e5b0192e4f3f9b32"},
+ {file = "aiohttp-3.14.3-cp310-cp310-win32.whl", hash = "sha256:a3e22975f905b89a55a488c2a08f2fdb2186175349e917d48985cc468a3d4c6e"},
+ {file = "aiohttp-3.14.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdd0e2834dce1a26c1bbe26464861e16bbe217042cbff619247c11594472518c"},
+ {file = "aiohttp-3.14.3-cp310-cp310-win_arm64.whl", hash = "sha256:eac645b09bcfdf73df7536331f0678c1086ea250981118ddb5199e17ccef72bb"},
+ {file = "aiohttp-3.14.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e568e14940c09955aa51f4e645b6daa18a581c5dcfcd73744dcc86a856e3ced3"},
+ {file = "aiohttp-3.14.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:54cfcdee2770dac994417cbb0ee1f3eb0e7cb6b30c79bf44f2c02ff79ec5124a"},
+ {file = "aiohttp-3.14.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:21c016079415ed3fd676963e9793700a566d85dbbd6bfc564b9b2d209147dcc8"},
+ {file = "aiohttp-3.14.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d6088ec9894113802bddb3c09e974929aed2c7b3a8c456219b8aab4481f1a239"},
+ {file = "aiohttp-3.14.3-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:16ea7e24c309fb7c0bbd505d149abe4fe4dccfb8db911db7dbec0921bc889a6f"},
+ {file = "aiohttp-3.14.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:56f355e79f71aef2a85c80305cc915f894b170dba76de5fe84f6351939b83c06"},
+ {file = "aiohttp-3.14.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:18c441d0a8fca6de8d1f546849b9f0ab20d435993e2c5b59562b2fae6be2f929"},
+ {file = "aiohttp-3.14.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:53e7b4ce82b54a8bcc71b3b67a5cbd177ca1d7f592cbc92cd38b7349f73482db"},
+ {file = "aiohttp-3.14.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f55119f7bf25f49ed210f6096090715da24f2943c62102448915fde3c62877ce"},
+ {file = "aiohttp-3.14.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9aa6e61fdf20105c4144e755bd586008ff450791d67b1c8146fdc15959c4d51c"},
+ {file = "aiohttp-3.14.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:ccd4893707b3e2a13e39c90d43cf80edf2e4d0457935bcc103bf2346214c3f15"},
+ {file = "aiohttp-3.14.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:b2466434105a4e03113c36ec775cc2ebe6676b62eae326fa670bb607ef788c1c"},
+ {file = "aiohttp-3.14.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:ba59d59aba08ac02fc03b0c8983ccd5ee39a199d0552ce9e6d2b4845b34d59ae"},
+ {file = "aiohttp-3.14.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:ed099d105449c4f9e84f24af203cd131349d4761d8813fa7e02c32e7128cd910"},
+ {file = "aiohttp-3.14.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:152516815ef926786a0b6ae2b8f1fd2e0c71582dee0b435636865316fd4891b7"},
+ {file = "aiohttp-3.14.3-cp311-cp311-win32.whl", hash = "sha256:a4af35c443e0b1a1bd6a8af3f3485d7fda15c142751a00f3ff8090f0b93346fa"},
+ {file = "aiohttp-3.14.3-cp311-cp311-win_amd64.whl", hash = "sha256:e1e74298bab6ee0d6e749ed4fd1901c7e604bdda32c03d787a2cc71c46d0433d"},
+ {file = "aiohttp-3.14.3-cp311-cp311-win_arm64.whl", hash = "sha256:03cd2bde3d7f085b64e549c985f4bb928cad7e8ecf5323bfca320db548d81b39"},
+ {file = "aiohttp-3.14.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:39aded8c7f3b935b54aab1d8d73c70ec0ee2d3ec3b943e0e86611bc150ba47f5"},
+ {file = "aiohttp-3.14.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5bcb6ff3fdab1258a192679ff1a05d44f59626430aa05cd1a9d2447423599228"},
+ {file = "aiohttp-3.14.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:617105e2c3018ee38d0c8ce5ee3c84f621a6d8b9f723202aacaff28449ca91ee"},
+ {file = "aiohttp-3.14.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f631fe87a6f30df5fbe6d79640b25e4cffb38c31c7fb6f10871517b84b0f8c1a"},
+ {file = "aiohttp-3.14.3-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a94dbaae5ae27bd849c93570669bff91e0510f33a80805738e3de72a7be0447b"},
+ {file = "aiohttp-3.14.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8f2f1c4c032c7cedd7d8da6f54c97b70266c6570c3108d3fdffee7188bb70529"},
+ {file = "aiohttp-3.14.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ea05e1f97ceea523942d9b2a7d7c0359d781d683d6b043f5943a602b14da4787"},
+ {file = "aiohttp-3.14.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:543906c127fb1d929b95076db19b83fa2d46751006ff1e23b093aa5ac4d8db42"},
+ {file = "aiohttp-3.14.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0a5ff2dfbb9ce645fa5b8ef3e02c6c0b9cc3f6030ff863d0c51fffc50cb5541b"},
+ {file = "aiohttp-3.14.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:041badb8f84396357c4d3ad26de6afd7a32b112f43d3c63045c0c8278cfd2043"},
+ {file = "aiohttp-3.14.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:530125ee1163c4219af35dc3aa1206e541e7b31b6efc1a3f93b70a136f65d427"},
+ {file = "aiohttp-3.14.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c8653fd547c93a61aadc612007790f5555cdd18946fa48cf45e26d8ea4ea473d"},
+ {file = "aiohttp-3.14.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:89176250f686cb9853c0fb7ead90e639e915b84a6f43eedc2a4e7ec21f1037f0"},
+ {file = "aiohttp-3.14.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3a26434dafe408229ff3403458ca58de24fb51936504decac49ce6755f77e59d"},
+ {file = "aiohttp-3.14.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d1558173930a5a8d3069cee5c92fc91c87c4dbcb099debbb3622053717145a19"},
+ {file = "aiohttp-3.14.3-cp312-cp312-win32.whl", hash = "sha256:16100ad3ab8d649fdfbee87602d9d2dcdca9df0b9eda8a1b5fdc0d41f96da559"},
+ {file = "aiohttp-3.14.3-cp312-cp312-win_amd64.whl", hash = "sha256:33a2d7c28d33797a2e99923dffa63f83d908a19b6bf26cfe80fa790aa5e1a75a"},
+ {file = "aiohttp-3.14.3-cp312-cp312-win_arm64.whl", hash = "sha256:362a3fd481769cac1a824514bcd86fda51c65e8fe6e051099e008fddde6db17c"},
+ {file = "aiohttp-3.14.3-cp313-cp313-android_21_arm64_v8a.whl", hash = "sha256:2e9878ae68e4a5f1c0abe4dd497dbc3d51946f5837b56759e2a02e78fa90ef86"},
+ {file = "aiohttp-3.14.3-cp313-cp313-android_21_x86_64.whl", hash = "sha256:f3d2669fe7dec7fc359ecdb5984b29b50d85d5d00f8c1cb61de4f4a24ee42627"},
+ {file = "aiohttp-3.14.3-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:cc7cb243a68167172f48c1fd43cee91ec4b1d40cefd190edd43369d1a6bc9c82"},
+ {file = "aiohttp-3.14.3-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:78253b573e6ffab5028924fc98bc281aae05445969982a10864bc360dea2016c"},
+ {file = "aiohttp-3.14.3-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:7041d52c3a7fa20c9e8c182b534704abb19502c8bdcbde7ab23bfda6f642394f"},
+ {file = "aiohttp-3.14.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ac74facc01463f138b0da5580329cfcc82818dea5656e83ddcd11268fc12ff80"},
+ {file = "aiohttp-3.14.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d6218d92e450824e9b4881f44e8c09f1853b490f9a64130801024a4793b1b3b0"},
+ {file = "aiohttp-3.14.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:11fb37ef075669eee52ab1928fbf6e1741fada40409fa309ebde9607a962aebf"},
+ {file = "aiohttp-3.14.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:55bdcc472aafe2de4a253045cc128007a64f1e0264fb675791e132ea5edaa3bd"},
+ {file = "aiohttp-3.14.3-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c39846c3aad97a8530c89d7a3869a8f8e9e3762c6ac0504481e5c80948f7e807"},
+ {file = "aiohttp-3.14.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5895ef58c4620afe02fa16044f023dc4dafec08158f9d08874a46a7dbc0341b8"},
+ {file = "aiohttp-3.14.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fa9467a8113aa69d3d7c55a70ef0b7c636010a40993f3df9d9d0d73b3eb7ef24"},
+ {file = "aiohttp-3.14.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d7d2deec16eeedf55f2c7cf75b521ea3856a5177e123844f8fd0f114ce252cb5"},
+ {file = "aiohttp-3.14.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:dd54d0e8717de95939766febac482ac0474d8ac3b048115f9f2b1d23a16e7db4"},
+ {file = "aiohttp-3.14.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:df82f3787c940c94986b34222d59c9e38843fba85139f36e85255a82ad5355a9"},
+ {file = "aiohttp-3.14.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:42a67efc36300d052fb4508a53e8b6901b9284b599ae63945c377569c5fcc1e1"},
+ {file = "aiohttp-3.14.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7a75aa63cbf9b21cfaf60dc2657e19df2c2867d91707d653fee171ffeedd1371"},
+ {file = "aiohttp-3.14.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:e92eb8acc45eb6a9f4935071a77edf5b85cc6f8dfad5cd99e97653c26593cdde"},
+ {file = "aiohttp-3.14.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b014a6ed7cf912e787149fdc529166d3ceabac23f26efeea3158c9aba2354e7e"},
+ {file = "aiohttp-3.14.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3d4f72af88ac2474bb5bca640030320e3d38a0163a1d7533500e87be458eef71"},
+ {file = "aiohttp-3.14.3-cp313-cp313-win32.whl", hash = "sha256:5f08ec777f35ee70720233b8b9811d3bb5d728137f30ac91b7457709c3261ac0"},
+ {file = "aiohttp-3.14.3-cp313-cp313-win_amd64.whl", hash = "sha256:dff9461ec275f22135650d5ba4b4931a11f3958df7dfbb8db630000d4dee0883"},
+ {file = "aiohttp-3.14.3-cp313-cp313-win_arm64.whl", hash = "sha256:ddcac3c6b382e81f1dd0499199d4136b877beb4cb5ef770bbbfba56c4b8f55d2"},
+ {file = "aiohttp-3.14.3-cp314-cp314-android_24_arm64_v8a.whl", hash = "sha256:49f7325beb0f85ef4aef5f48f490269575f83e6e2acad00a1d80b807eb027062"},
+ {file = "aiohttp-3.14.3-cp314-cp314-android_24_x86_64.whl", hash = "sha256:e3be98a7c30b8c25d573dafba7171d66dfb05ee6a9070fc46535464ff97700a6"},
+ {file = "aiohttp-3.14.3-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:614c61d478b83953e261d02bb2df750f17227cd33ef8002945bf5aebbde21919"},
+ {file = "aiohttp-3.14.3-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:1caa7b0d05f3e3a36f87788c59e970a7ee1cefcfcbb924a9f138c4a6551c9cb7"},
+ {file = "aiohttp-3.14.3-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:dfa68deb2a443bdaa3ea5297b0699c1464f08aef3812b486d1348eee61b07dc0"},
+ {file = "aiohttp-3.14.3-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:e72ee89e28d907a18f46959b4eb0bb06701cc7f8cf4366e00029e2ccfaaf5924"},
+ {file = "aiohttp-3.14.3-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:ad4c8b7488d745d2ca4838ebd8ae5ba9b56341d30b1da43640e4ce87f9f49646"},
+ {file = "aiohttp-3.14.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:db332af25642007330fca8be5c4d194caf2bea7a7fc84415aff3497af5dfee6b"},
+ {file = "aiohttp-3.14.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:25bd2708db6bdf6a6630dd37bdcdfcb47c4434d22ac69c64665b802910140b30"},
+ {file = "aiohttp-3.14.3-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:cef89a58e628c4efcac3275c2d68083f82426dcdc89c1492a6f654f9f7ea6ab9"},
+ {file = "aiohttp-3.14.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c23ec8ee9d5ab2f5421f9c7fffce208435607af27fd46d4a44e031954352838f"},
+ {file = "aiohttp-3.14.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e2667f0bbe7eb6c74eae5e9691441ad186e5845ca3cff63230fc09c4e7514f5d"},
+ {file = "aiohttp-3.14.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18cb43369747b2ae007bd2655fb8e63a099c2ff1d207962943636dac989b3147"},
+ {file = "aiohttp-3.14.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d77640cc618c1d99fc4f8589c0f24a730adfa54eb1e57ef7bf0c8dfb78da898c"},
+ {file = "aiohttp-3.14.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:53e5179d8abb5710f8e83ba207c41c8d1261fcffd4616500e15ca2b7a33be10a"},
+ {file = "aiohttp-3.14.3-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:cd817772b2fcf2b8c0905795318485f9ec16eae60b29feb7f4c77085311637f0"},
+ {file = "aiohttp-3.14.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:4e3ac92d90e92773b2362d506068e9a948192bd553e743c5b2429e28527c8661"},
+ {file = "aiohttp-3.14.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:3f42e9b78301f11c8f861746175d8b9c1ccef713fcad9eab396e2f6db8ed4a22"},
+ {file = "aiohttp-3.14.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:9d9edccfe496b476db5f398d97b865e9a6752bcf8aec4eef8390ce20fb64bb41"},
+ {file = "aiohttp-3.14.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:1c5ec8fb1bcc31a8466f74aaf26c345d5c386fa4bd08a3f0eb9c7a4a3fe8b5bf"},
+ {file = "aiohttp-3.14.3-cp314-cp314-win32.whl", hash = "sha256:38901a84da3ce22249f6e860bf8f90d141bcab7da090cc398f8bb58c0e44b7da"},
+ {file = "aiohttp-3.14.3-cp314-cp314-win_amd64.whl", hash = "sha256:8b3b60de05f3dcb6f6a00f818bb2ec781cee4de0645f59ccaf99b1d1823b6100"},
+ {file = "aiohttp-3.14.3-cp314-cp314-win_arm64.whl", hash = "sha256:1576145bdceeb92382d899751e12743a3a5b8e460a841e3e50543859e54864dc"},
+ {file = "aiohttp-3.14.3-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:8800c996b01c2772a783e3e46f3e1abd5823029adca0df54231960de9bfefa5b"},
+ {file = "aiohttp-3.14.3-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:ebe8e504f058fe91223351cecd2d9d6946c9d241bb0250d898ffbdf584cc72b0"},
+ {file = "aiohttp-3.14.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:30402d03a7c0ff52bce290b57e564e9079fd9d0cb545c8aba73f86a103162d2e"},
+ {file = "aiohttp-3.14.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9fc7b5bfec6573f3ae844f457fdde5adeb713f8b8e4a81ad64fc207b49383716"},
+ {file = "aiohttp-3.14.3-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8a5fd34f7f7410d1730d5c2ba873cacb2eed3fede366feb268a70ba22581ed8f"},
+ {file = "aiohttp-3.14.3-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:270d3dace9ca2f10f0da5d8ebe519b7a310fc6112ed916e32df5866df0888553"},
+ {file = "aiohttp-3.14.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3ae5b3a59436d089b5395d910121a390feed4d00578eb95a0fd1a329fe963100"},
+ {file = "aiohttp-3.14.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2498f0fe69ead802f9675beca44a7c21c62fdaa4ec5145ea1c3ad6edbee29f85"},
+ {file = "aiohttp-3.14.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a0dc483c00da8b673abbb367eb6f8d8f4bcec30eb58529ea13cb42e7fd2dfa33"},
+ {file = "aiohttp-3.14.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c7d3a97c678d34fc5b59da671ee9cd630096ddc643e7b5a30d54a2a6f3574d3f"},
+ {file = "aiohttp-3.14.3-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:f8fb78a83c9e5f741ca3a68cfb455c1f5bb83b4e7249a3848b3cd78d0a8563b0"},
+ {file = "aiohttp-3.14.3-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:74ab5b6a9fb13e873e5a90946588baecaf488745e1db1a4a5c433f971f035098"},
+ {file = "aiohttp-3.14.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:bd52f811e65f6fb634b1047159657c98f52b407f8efec907bcfc09da9a4c0a25"},
+ {file = "aiohttp-3.14.3-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:f0f177d1b195b9e06376cfd7d308d8a1b920909a609d03ac82a8c73bbb16d3b9"},
+ {file = "aiohttp-3.14.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:498c6c623134f8e09a3c4e60bcd607a0b4590dd7dbf08dd40851b27cbb520ccb"},
+ {file = "aiohttp-3.14.3-cp314-cp314t-win32.whl", hash = "sha256:b304db572b4368edd8dda8a2274f73156fe15558fca4a917cb8a09fc47af5963"},
+ {file = "aiohttp-3.14.3-cp314-cp314t-win_amd64.whl", hash = "sha256:b20032766aedf6261c7a566585a40867d092ac03a0d81592d5370ef9b054f99b"},
+ {file = "aiohttp-3.14.3-cp314-cp314t-win_arm64.whl", hash = "sha256:2e1161602f45a54de2ce0905243a95f58cb42dcd378402f3697f5e0b21e9d2e7"},
+ {file = "aiohttp-3.14.3.tar.gz", hash = "sha256:9491196535a88924a60afd5b5f434b5b203b6cc616250878dbdb223a8f7844bc"},
]
[package.dependencies]
@@ -151,7 +155,7 @@ typing_extensions = {version = ">=4.4", markers = "python_version < \"3.13\""}
yarl = ">=1.17.0,<2.0"
[package.extras]
-speedups = ["Brotli (>=1.2)", "aiodns (>=3.3.0)", "backports.zstd", "brotlicffi (>=1.2)"]
+speedups = ["Brotli (>=1.2) ; platform_python_implementation == \"CPython\" and sys_platform != \"android\" and sys_platform != \"ios\"", "aiodns (>=3.3.0) ; sys_platform != \"android\" and sys_platform != \"ios\"", "backports.zstd ; platform_python_implementation == \"CPython\" and python_version < \"3.14\" and sys_platform != \"android\" and sys_platform != \"ios\"", "brotlicffi (>=1.2) ; platform_python_implementation != \"CPython\""]
[[package]]
name = "aiosignal"
@@ -159,6 +163,8 @@ version = "1.4.0"
description = "aiosignal: a list of registered asynchronous callbacks"
optional = true
python-versions = ">=3.9"
+groups = ["main"]
+markers = "extra == \"aiohttp\""
files = [
{file = "aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e"},
{file = "aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7"},
@@ -170,24 +176,26 @@ typing-extensions = {version = ">=4.2", markers = "python_version < \"3.13\""}
[[package]]
name = "annotated-types"
-version = "0.7.0"
+version = "0.8.0"
description = "Reusable constraint types to use with typing.Annotated"
optional = false
-python-versions = ">=3.8"
+python-versions = ">=3.10"
+groups = ["main"]
files = [
- {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"},
- {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"},
+ {file = "annotated_types-0.8.0-py3-none-any.whl", hash = "sha256:f072f4d804ea359e4eaf198b1af7a8b0943881a87f31bb764f8bf219bb9419e0"},
+ {file = "annotated_types-0.8.0.tar.gz", hash = "sha256:13b2beaad985e05e2d6407ee4c4f35590b11f8d693a258a561055cac8f64cab7"},
]
[[package]]
name = "anyio"
-version = "4.13.0"
+version = "4.14.2"
description = "High-level concurrency and networking framework on top of asyncio or Trio"
optional = false
python-versions = ">=3.10"
+groups = ["main"]
files = [
- {file = "anyio-4.13.0-py3-none-any.whl", hash = "sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708"},
- {file = "anyio-4.13.0.tar.gz", hash = "sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc"},
+ {file = "anyio-4.14.2-py3-none-any.whl", hash = "sha256:9f505dda5ac9f0c8309b5e8bd445a8c2bf7246f3ce950121e45ea15bc41d1494"},
+ {file = "anyio-4.14.2.tar.gz", hash = "sha256:cfa139f3ed1a23ee8f88a145ddb5ac7605b8bbfd8592baacd7ce3d8bb4313c7f"},
]
[package.dependencies]
@@ -204,6 +212,8 @@ version = "5.0.1"
description = "Timeout context manager for asyncio programs"
optional = true
python-versions = ">=3.8"
+groups = ["main"]
+markers = "python_version == \"3.10\" and extra == \"aiohttp\""
files = [
{file = "async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c"},
{file = "async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3"},
@@ -215,6 +225,8 @@ version = "26.1.0"
description = "Classes Without Boilerplate"
optional = true
python-versions = ">=3.9"
+groups = ["main"]
+markers = "extra == \"aiohttp\""
files = [
{file = "attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309"},
{file = "attrs-26.1.0.tar.gz", hash = "sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32"},
@@ -226,6 +238,8 @@ version = "1.2.0"
description = "Backport of asyncio.Runner, a context manager that controls event loop life cycle."
optional = false
python-versions = "<3.11,>=3.8"
+groups = ["dev"]
+markers = "python_version == \"3.10\""
files = [
{file = "backports_asyncio_runner-1.2.0-py3-none-any.whl", hash = "sha256:0da0a936a8aeb554eccb426dc55af3ba63bcdc69fa1a600b5bb305413a4477b5"},
{file = "backports_asyncio_runner-1.2.0.tar.gz", hash = "sha256:a5aa7b2b7d8f8bfcaa2b57313f70792df84e32a2a746f585213373f900b42162"},
@@ -233,13 +247,14 @@ files = [
[[package]]
name = "certifi"
-version = "2026.5.20"
+version = "2026.7.22"
description = "Python package for providing Mozilla's CA Bundle."
optional = false
python-versions = ">=3.7"
+groups = ["main"]
files = [
- {file = "certifi-2026.5.20-py3-none-any.whl", hash = "sha256:3c52e209ba0a4ad7aebe60436a4ab349c39e1e602e8c134221e546902ad25897"},
- {file = "certifi-2026.5.20.tar.gz", hash = "sha256:69dea482ab64caa7b9f6aba1c6bf48bb6a5448d1c0f1b17ab42ad8c763a5344d"},
+ {file = "certifi-2026.7.22-py3-none-any.whl", hash = "sha256:62f22742b58a1a33014a2b6b706588a8d7e2a88ae7bd1a6ebe8c992928483775"},
+ {file = "certifi-2026.7.22.tar.gz", hash = "sha256:741e2c3b351ddf169a738da9f2c048608ff7f2c5cc02f1ebc6b118bb090d5d55"},
]
[[package]]
@@ -248,6 +263,8 @@ version = "0.4.6"
description = "Cross-platform colored terminal text."
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
+groups = ["dev"]
+markers = "sys_platform == \"win32\""
files = [
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
@@ -259,6 +276,8 @@ version = "1.3.1"
description = "Backport of PEP 654 (exception groups)"
optional = false
python-versions = ">=3.7"
+groups = ["main", "dev"]
+markers = "python_version == \"3.10\""
files = [
{file = "exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598"},
{file = "exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219"},
@@ -276,6 +295,7 @@ version = "2.1.2"
description = "execnet: rapid multi-Python deployment"
optional = false
python-versions = ">=3.8"
+groups = ["dev"]
files = [
{file = "execnet-2.1.2-py3-none-any.whl", hash = "sha256:67fba928dd5a544b783f6056f449e5e3931a5c378b128bc18501f7ea79e296ec"},
{file = "execnet-2.1.2.tar.gz", hash = "sha256:63d83bfdd9a23e35b9c6a3261412324f964c2ec8dcd8d3c6916ee9373e0befcd"},
@@ -290,6 +310,8 @@ version = "1.8.0"
description = "A list-like structure which implements collections.abc.MutableSequence"
optional = true
python-versions = ">=3.9"
+groups = ["main"]
+markers = "extra == \"aiohttp\""
files = [
{file = "frozenlist-1.8.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b37f6d31b3dcea7deb5e9696e529a6aa4a898adc33db82da12e4c60a7c4d2011"},
{file = "frozenlist-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef2b7b394f208233e471abc541cc6991f907ffd47dc72584acee3147899d6565"},
@@ -429,6 +451,7 @@ version = "0.16.0"
description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1"
optional = false
python-versions = ">=3.8"
+groups = ["main"]
files = [
{file = "h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86"},
{file = "h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1"},
@@ -440,6 +463,7 @@ version = "1.0.9"
description = "A minimal low-level HTTP client."
optional = false
python-versions = ">=3.8"
+groups = ["main"]
files = [
{file = "httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55"},
{file = "httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8"},
@@ -461,6 +485,7 @@ version = "0.28.1"
description = "The next generation HTTP client."
optional = false
python-versions = ">=3.8"
+groups = ["main"]
files = [
{file = "httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad"},
{file = "httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc"},
@@ -473,7 +498,7 @@ httpcore = "==1.*"
idna = "*"
[package.extras]
-brotli = ["brotli", "brotlicffi"]
+brotli = ["brotli ; platform_python_implementation == \"CPython\"", "brotlicffi ; platform_python_implementation != \"CPython\""]
cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"]
http2 = ["h2 (>=3,<5)"]
socks = ["socksio (==1.*)"]
@@ -481,13 +506,15 @@ zstd = ["zstandard (>=0.18.0)"]
[[package]]
name = "httpx-aiohttp"
-version = "0.1.8"
+version = "0.1.12"
description = "Aiohttp transport for HTTPX"
optional = true
python-versions = ">=3.8"
+groups = ["main"]
+markers = "extra == \"aiohttp\""
files = [
- {file = "httpx_aiohttp-0.1.8-py3-none-any.whl", hash = "sha256:b7bd958d1331f3759a38a0ba22ad29832cb63ca69498c17735228055bf78fa7e"},
- {file = "httpx_aiohttp-0.1.8.tar.gz", hash = "sha256:756c5e74cdb568c3248ba63fe82bfe8bbe64b928728720f7eaac64b3cf46f308"},
+ {file = "httpx_aiohttp-0.1.12-py3-none-any.whl", hash = "sha256:5b0eac39a7f360fa7867a60bcb46bb1024eada9c01cbfecdb54dc1edb3fb7141"},
+ {file = "httpx_aiohttp-0.1.12.tar.gz", hash = "sha256:81feec51fd82c0ecfa0e9aaf1b1a6c2591260d5e2bcbeb7eb0277a78e610df2c"},
]
[package.dependencies]
@@ -500,6 +527,7 @@ version = "3.18"
description = "Internationalized Domain Names in Applications (IDNA)"
optional = false
python-versions = ">=3.9"
+groups = ["main"]
files = [
{file = "idna-3.18-py3-none-any.whl", hash = "sha256:7f952cbe720b688055e3f87de14f5c3e5fdaa8bc3928985c4077ca689de849a2"},
{file = "idna-3.18.tar.gz", hash = "sha256:ffb385a7e039654cef1ab9ef32c6fafe283c0c0467bba1d9029738ce4a14a848"},
@@ -514,6 +542,7 @@ version = "2.3.0"
description = "brain-dead simple config-ini parsing"
optional = false
python-versions = ">=3.10"
+groups = ["dev"]
files = [
{file = "iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12"},
{file = "iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730"},
@@ -525,6 +554,8 @@ version = "6.7.1"
description = "multidict implementation"
optional = true
python-versions = ">=3.9"
+groups = ["main"]
+markers = "extra == \"aiohttp\""
files = [
{file = "multidict-6.7.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c93c3db7ea657dd4637d57e74ab73de31bccefe144d3d4ce370052035bc85fb5"},
{file = "multidict-6.7.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:974e72a2474600827abaeda71af0c53d9ebbc3c2eb7da37b37d7829ae31232d8"},
@@ -683,6 +714,7 @@ version = "1.13.0"
description = "Optional static typing for Python"
optional = false
python-versions = ">=3.8"
+groups = ["dev"]
files = [
{file = "mypy-1.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6607e0f1dd1fb7f0aca14d936d13fd19eba5e17e1cd2a14f808fa5f8f6d8f60a"},
{file = "mypy-1.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8a21be69bd26fa81b1f80a61ee7ab05b076c674d9b18fb56239d72e21d9f4c80"},
@@ -736,6 +768,7 @@ version = "1.1.0"
description = "Type system extensions for programs checked with the mypy type checker."
optional = false
python-versions = ">=3.8"
+groups = ["dev"]
files = [
{file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"},
{file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"},
@@ -743,13 +776,14 @@ files = [
[[package]]
name = "packaging"
-version = "26.2"
+version = "26.3"
description = "Core utilities for Python packages"
optional = false
-python-versions = ">=3.8"
+python-versions = ">=3.9"
+groups = ["dev"]
files = [
- {file = "packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e"},
- {file = "packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661"},
+ {file = "packaging-26.3-py3-none-any.whl", hash = "sha256:d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c"},
+ {file = "packaging-26.3.tar.gz", hash = "sha256:94edc256424af38762eb31306eed28beb9f0efc50a8837492c9d6fd6004aed79"},
]
[[package]]
@@ -758,6 +792,7 @@ version = "1.6.0"
description = "plugin and hook calling mechanisms for python"
optional = false
python-versions = ">=3.9"
+groups = ["dev"]
files = [
{file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"},
{file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"},
@@ -773,6 +808,8 @@ version = "0.5.2"
description = "Accelerated property cache"
optional = true
python-versions = ">=3.10"
+groups = ["main"]
+markers = "extra == \"aiohttp\""
files = [
{file = "propcache-0.5.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d5a81be28596d6559f6131ef33e10200de6e17643b3c74ce03f9eb103be6ae8b"},
{file = "propcache-0.5.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:29cbaac5ea0212663e6845e04b5e188d5a6ae6dd919810ac835bf1d3b42c3f4c"},
@@ -903,6 +940,7 @@ version = "2.13.4"
description = "Data validation using Python type hints"
optional = false
python-versions = ">=3.9"
+groups = ["main"]
files = [
{file = "pydantic-2.13.4-py3-none-any.whl", hash = "sha256:45a282cde31d808236fd7ea9d919b128653c8b38b393d1c4ab335c62924d9aba"},
{file = "pydantic-2.13.4.tar.gz", hash = "sha256:c40756b57adaa8b1efeeced5c196f3f3b7c435f90e84ea7f443901bec8099ef6"},
@@ -916,7 +954,7 @@ typing-inspection = ">=0.4.2"
[package.extras]
email = ["email-validator (>=2.0.0)"]
-timezone = ["tzdata"]
+timezone = ["tzdata ; python_version >= \"3.9\" and platform_system == \"Windows\""]
[[package]]
name = "pydantic-core"
@@ -924,6 +962,7 @@ version = "2.46.4"
description = "Core functionality for Pydantic validation and serialization"
optional = false
python-versions = ">=3.9"
+groups = ["main"]
files = [
{file = "pydantic_core-2.46.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:a396dcc17e5a0b164dbe026896245a4fa9ff402edca1dff0be3d53a517f74de4"},
{file = "pydantic_core-2.46.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:da4b951fe36dc7c3a1ccb4e3cd1747c3542b8c9ceede8fc86cae054e764485f5"},
@@ -1056,6 +1095,7 @@ version = "2.20.0"
description = "Pygments is a syntax highlighting package written in Python."
optional = false
python-versions = ">=3.9"
+groups = ["dev"]
files = [
{file = "pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176"},
{file = "pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f"},
@@ -1066,13 +1106,14 @@ windows-terminal = ["colorama (>=0.4.6)"]
[[package]]
name = "pytest"
-version = "9.0.3"
+version = "9.1.1"
description = "pytest: simple powerful testing with Python"
optional = false
python-versions = ">=3.10"
+groups = ["dev"]
files = [
- {file = "pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9"},
- {file = "pytest-9.0.3.tar.gz", hash = "sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c"},
+ {file = "pytest-9.1.1-py3-none-any.whl", hash = "sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c"},
+ {file = "pytest-9.1.1.tar.gz", hash = "sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313"},
]
[package.dependencies]
@@ -1093,6 +1134,7 @@ version = "1.4.0"
description = "Pytest support for asyncio"
optional = false
python-versions = ">=3.10"
+groups = ["dev"]
files = [
{file = "pytest_asyncio-1.4.0-py3-none-any.whl", hash = "sha256:933ca923a23075a87fb7070c0ec272a6848489824d887c85c812670932835aa1"},
{file = "pytest_asyncio-1.4.0.tar.gz", hash = "sha256:c6c0d2259945122819f171a32ecea2c349ead889ee28176caaf492143424be42"},
@@ -1113,6 +1155,7 @@ version = "3.8.0"
description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs"
optional = false
python-versions = ">=3.9"
+groups = ["dev"]
files = [
{file = "pytest_xdist-3.8.0-py3-none-any.whl", hash = "sha256:202ca578cfeb7370784a8c33d6d05bc6e13b4f25b5053c30a152269fd10f0b88"},
{file = "pytest_xdist-3.8.0.tar.gz", hash = "sha256:7e578125ec9bc6050861aa93f2d59f1d8d085595d6551c2c90b6f4fad8d3a9f1"},
@@ -1133,6 +1176,7 @@ version = "2.9.0.post0"
description = "Extensions to the standard Python datetime module"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
+groups = ["dev"]
files = [
{file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"},
{file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"},
@@ -1147,6 +1191,7 @@ version = "0.11.5"
description = "An extremely fast Python linter and code formatter, written in Rust."
optional = false
python-versions = ">=3.7"
+groups = ["dev"]
files = [
{file = "ruff-0.11.5-py3-none-linux_armv6l.whl", hash = "sha256:2561294e108eb648e50f210671cc56aee590fb6167b594144401532138c66c7b"},
{file = "ruff-0.11.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ac12884b9e005c12d0bd121f56ccf8033e1614f736f766c118ad60780882a077"},
@@ -1174,6 +1219,7 @@ version = "1.17.0"
description = "Python 2 and 3 compatibility utilities"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
+groups = ["dev"]
files = [
{file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"},
{file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"},
@@ -1185,6 +1231,8 @@ version = "2.4.1"
description = "A lil' TOML parser"
optional = false
python-versions = ">=3.8"
+groups = ["dev"]
+markers = "python_version == \"3.10\""
files = [
{file = "tomli-2.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f8f0fc26ec2cc2b965b7a3b87cd19c5c6b8c5e5f436b984e85f486d652285c30"},
{file = "tomli-2.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4ab97e64ccda8756376892c53a72bd1f964e519c77236368527f758fbc36a53a"},
@@ -1237,39 +1285,42 @@ files = [
[[package]]
name = "types-python-dateutil"
-version = "2.9.0.20260518"
+version = "2.9.0.20260807"
description = "Typing stubs for python-dateutil"
optional = false
python-versions = ">=3.10"
+groups = ["dev"]
files = [
- {file = "types_python_dateutil-2.9.0.20260518-py3-none-any.whl", hash = "sha256:d6a9c5bd0de61460c8fdef8ab2b400f956a1a1075cce08d4e2b4434e478c50b8"},
- {file = "types_python_dateutil-2.9.0.20260518.tar.gz", hash = "sha256:51f02dc03b61c7f6a07df45797d4dfe8a1aa47f0b7db9ad89f6fd3a1a70e1b51"},
+ {file = "types_python_dateutil-2.9.0.20260807-py3-none-any.whl", hash = "sha256:54aa3707350ed7a9cc0776fd2f6739679d6967d11b40150985e81edcb86df4db"},
+ {file = "types_python_dateutil-2.9.0.20260807.tar.gz", hash = "sha256:e0b8a90d464c8684c66b7b8e4556d9074afdddcc56ca45323f0987134f9e7034"},
]
[[package]]
name = "typing-extensions"
-version = "4.15.0"
+version = "4.16.0"
description = "Backported and Experimental Type Hints for Python 3.9+"
optional = false
python-versions = ">=3.9"
+groups = ["main", "dev"]
files = [
- {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"},
- {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"},
+ {file = "typing_extensions-4.16.0-py3-none-any.whl", hash = "sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8"},
+ {file = "typing_extensions-4.16.0.tar.gz", hash = "sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5"},
]
[[package]]
name = "typing-inspection"
-version = "0.4.2"
+version = "0.4.4"
description = "Runtime typing introspection tools"
optional = false
-python-versions = ">=3.9"
+python-versions = ">=3.10"
+groups = ["main"]
files = [
- {file = "typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7"},
- {file = "typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464"},
+ {file = "typing_inspection-0.4.4-py3-none-any.whl", hash = "sha256:65b8397ba37ccbce054456aaccddfc91e6e3083c92824df348d96ca832f3f147"},
+ {file = "typing_inspection-0.4.4.tar.gz", hash = "sha256:547274fa6b0a561ccf549cc9524b999a578e737d015d8709d021f9d0d13bea47"},
]
[package.dependencies]
-typing-extensions = ">=4.12.0"
+typing-extensions = ">=4.15.0"
[[package]]
name = "urllib3"
@@ -1277,128 +1328,131 @@ version = "2.7.0"
description = "HTTP library with thread-safe connection pooling, file post, and more."
optional = false
python-versions = ">=3.10"
+groups = ["dev"]
files = [
{file = "urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897"},
{file = "urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c"},
]
[package.extras]
-brotli = ["brotli (>=1.2.0)", "brotlicffi (>=1.2.0.0)"]
+brotli = ["brotli (>=1.2.0) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=1.2.0.0) ; platform_python_implementation != \"CPython\""]
h2 = ["h2 (>=4,<5)"]
socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"]
-zstd = ["backports-zstd (>=1.0.0)"]
+zstd = ["backports-zstd (>=1.0.0) ; python_version < \"3.14\""]
[[package]]
name = "yarl"
-version = "1.24.2"
+version = "1.24.5"
description = "Yet another URL library"
optional = true
python-versions = ">=3.10"
+groups = ["main"]
+markers = "extra == \"aiohttp\""
files = [
- {file = "yarl-1.24.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5249a113065c2b7a958bc699759e359cd61cfc81e3069662208f48f191b7ed12"},
- {file = "yarl-1.24.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7f4425fa244fbf530b006d0c5f79ce920114cfff5b4f5f6056e669f8e160fdc0"},
- {file = "yarl-1.24.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:15c0b5e49d3c44e2a0b93e6a49476c5edad0a7686b92c395765a7ea775572a75"},
- {file = "yarl-1.24.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:246d32a53a947c8f0189f5d699cbd4c7036de45d9359e13ba238d1239678c727"},
- {file = "yarl-1.24.2-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:64480fb3e4d4ed9ed71c48a91a477384fc342a50ca30071d2f8a88d51d9c9413"},
- {file = "yarl-1.24.2-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:349de4701dc3760b6e876628423a8f147ef4f5599d10aba1e10702075d424ed9"},
- {file = "yarl-1.24.2-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d162677af8d5d3d6ebab8394b021f4d041ac107a4b705873148a77a49dc9e1b2"},
- {file = "yarl-1.24.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f5f5c6ec23a9043f2d139cc072f53dd23168d202a334b9b2fda8de4c3e890d90"},
- {file = "yarl-1.24.2-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:60de6742447fbbf697f16f070b8a443f1b5fe6ca3826fbef9fe70ecd5328e643"},
- {file = "yarl-1.24.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:acf93187c3710e422368eb768aee98db551ec7c85adc250207a95c16548ab7ac"},
- {file = "yarl-1.24.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:f4b0352fd41fd34b6651934606268816afd6914d09626f9bcbbf018edb0afb3f"},
- {file = "yarl-1.24.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:6b208bb939099b4b297438da4e9b25357f0b1c791888669b963e45b203ea9f36"},
- {file = "yarl-1.24.2-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:4b85b8825e631295ff4bc8943f7471d54c533a9360bbe15ebb38e018b555bb8a"},
- {file = "yarl-1.24.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e26acf20c26cb4fefc631fdb75aca2a6b8fa8b7b5d7f204fb6a8f1e63c706f53"},
- {file = "yarl-1.24.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:819ca24f8eafcfb683c1bd5f44f2f488cea1274eb8944731ffd2e1f10f619342"},
- {file = "yarl-1.24.2-cp310-cp310-win_amd64.whl", hash = "sha256:5cb0f995a901c36be096ccbf4c673591c2faabbe96279598ffaec8c030f85bf4"},
- {file = "yarl-1.24.2-cp310-cp310-win_arm64.whl", hash = "sha256:f408eace7e22a68b467a0562e0d27d322f91fe3eaaa6f466b962c6cfaea9fa39"},
- {file = "yarl-1.24.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:36348bebb147b83818b9d7e673ea4debc75970afc6ffdc7e3975ad05ce5a58c1"},
- {file = "yarl-1.24.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1a97e42c8a2233f2f279ecadd9e4a037bcb5d813b78435e8eedd4db5a9e9708c"},
- {file = "yarl-1.24.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8d027d56f1035e339d1001ac33eceab5b2ec8e42e449787bb75e289fb9a5cd1d"},
- {file = "yarl-1.24.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a6377060e7927187a42b7eb202090cbe2b34933a4eeaf90e3bd9e33432e5cae"},
- {file = "yarl-1.24.2-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:17076578bce0049a5ce57d14ad1bded391b68a3b213e9b81b0097b090244999a"},
- {file = "yarl-1.24.2-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:50713f1d4d6be6375bb178bb43d140ee1acb8abe589cd723320b7925a275be1e"},
- {file = "yarl-1.24.2-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:34263e2fa8fb5bb63a0d97706cda38edbad62fddb58c7f12d6acbc092812aa50"},
- {file = "yarl-1.24.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:49016d82f032b1bd1e10b01078a7d29ae71bf468eeae0ea22df8bab691e60003"},
- {file = "yarl-1.24.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3f6d2c216318f8f32038ca3f72501ba08536f0fd18a36e858836b121b2deed9f"},
- {file = "yarl-1.24.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:08d3a33218e0c64393e7610284e770409a9c31c429b078bcb24096ed0a783b8f"},
- {file = "yarl-1.24.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:5d699376c4ca3cba49bbfae3a05b5b70ded572937171ce1e0b8d87118e2ba294"},
- {file = "yarl-1.24.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a1cab588b4fa14bea2e55ebea27478adfb05372f47573738e1acc4a36c0b05d2"},
- {file = "yarl-1.24.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:ec87ccc31bd21db7ad009d8572c127c1000f268517618a4cc09adba3c2a7f21c"},
- {file = "yarl-1.24.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:d1dd47a22843b212baa8d74f37796815d43bd046b42a0f41e9da433386c3136b"},
- {file = "yarl-1.24.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7b54b9c67c2b06bd7b9a77253d242124b9c95d2c02def5a1144001ee547dd9d5"},
- {file = "yarl-1.24.2-cp311-cp311-win_amd64.whl", hash = "sha256:f8fdbcff8b2c7c9284e60c196f693588598ddcee31e11c18e14949ce44519d45"},
- {file = "yarl-1.24.2-cp311-cp311-win_arm64.whl", hash = "sha256:b32c37a7a337e90822c45797bf3d79d60875cfcccd3ecc80e9f453d87026c122"},
- {file = "yarl-1.24.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b975866c184564c827e0877380f0dae57dcca7e52782128381b72feff6dfceb8"},
- {file = "yarl-1.24.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3b075301a2836a0e297b1b658cb6d6135df535d62efefdd60366bd589c2c82f2"},
- {file = "yarl-1.24.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8ae44649b00947634ab0dab2a374a638f52923a6e67083f2c156cd5cbd1a881d"},
- {file = "yarl-1.24.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:507cc19f0b45454e2d6dcd62ff7d062b9f77a2812404e62dbdaec05b50faa035"},
- {file = "yarl-1.24.2-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c4c17bad5a530912d2111825d3f05e89bab2dd376aaa8cbc77e449e6db63e576"},
- {file = "yarl-1.24.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f5f0cbb112838a4a293985b6ed73948a547dadcc1ba6d2089938e7abdedceef8"},
- {file = "yarl-1.24.2-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5ec8356b8a6afcf81fc7aeeef13b1ff7a49dec00f313394bbb9e83830d32ccd7"},
- {file = "yarl-1.24.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7e7ebcdef69dec6c6451e616f32b622a6d4a2e92b445c992f7c8e5274a6bbc4c"},
- {file = "yarl-1.24.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:47a55d6cf6db2f401017a9e96e5288844e5051911fb4e0c8311a3980f5e59a7d"},
- {file = "yarl-1.24.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3065657c80a2321225e804048597ad55658a7e76b32d6f5ee4074d04c50401db"},
- {file = "yarl-1.24.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:cb84b80d88e19ede158619b80813968713d8d008b0e2497a576e6a0557d50712"},
- {file = "yarl-1.24.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:990de4f680b1c217e77ff0d6aa0029f9eb79889c11fb3e9a3942c7eba29c1996"},
- {file = "yarl-1.24.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:abb8ec0323b80161e3802da3150ef660b41d0e9be2048b76a363d93eee992c2b"},
- {file = "yarl-1.24.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e7977781f83638a4c73e0f88425563d70173e0dfd90ac006a45c65036293ee3c"},
- {file = "yarl-1.24.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e30dd55825dc554ec5b66a94953b8eda8745926514c5089dfcacecb9c99b5bd1"},
- {file = "yarl-1.24.2-cp312-cp312-win_amd64.whl", hash = "sha256:7dafe10c12ddd4d120d528c4b5599c953bd7b12845347d507b95451195bb6cad"},
- {file = "yarl-1.24.2-cp312-cp312-win_arm64.whl", hash = "sha256:044a09d8401fcf8681977faef6d286b8ade1e2d2e9dceda175d1cfa5ca496f30"},
- {file = "yarl-1.24.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:491ac9141decf49ee8030199e1ee251cdff0e131f25678817ff6aa5f837a3536"},
- {file = "yarl-1.24.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e89418f65eda18f99030386305bd44d7d504e328a7945db1ead514fbe03a0607"},
- {file = "yarl-1.24.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cdfcce633b4a4bb8281913c57fcafd4b5933fbc19111a5e3930bbd299d6102f1"},
- {file = "yarl-1.24.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:863297ddede92ee49024e9a9b11ecb59f310ca85b60d8537f56bed9bbb5b1986"},
- {file = "yarl-1.24.2-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:374423f70754a2c96942ede36a29d37dc6b0cb8f92f8d009ddf3ed78d3da5488"},
- {file = "yarl-1.24.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:33a29b5d00ccbf3219bb3e351d7875739c19481e030779f48cc46a7a71681a9b"},
- {file = "yarl-1.24.2-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a9532c57211730c515341af11fef6e9b61d157487272a096d0c04da445642592"},
- {file = "yarl-1.24.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:91e72cf093fd833483a97ee648e0c053c7c629f51ff4a0e7edd84f806b0c5617"},
- {file = "yarl-1.24.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b3177bc0a768ef3bacceb4f272632990b7bea352f1b2f1eee9d6d6ff16516f92"},
- {file = "yarl-1.24.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e196952aacaf3b232e265ff02980b64d483dc0972bd49bcb061171ff22ac203a"},
- {file = "yarl-1.24.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:204e7a61ce99919c0de1bf904ab5d7aa188a129ea8f690a8f76cfb6e2844dc44"},
- {file = "yarl-1.24.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4b156914620f0b9d78dc1adb3751141daee561cfec796088abb89ed49d220f1a"},
- {file = "yarl-1.24.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:8372a2b976cf70654b2be6619ab6068acabb35f724c0fda7b277fbf53d66a5cf"},
- {file = "yarl-1.24.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:f9a1e9b622ca284143aab5d885848686dcd85453bb1ca9abcdb7503e64dc0056"},
- {file = "yarl-1.24.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:810e19b685c8c3c5862f6a38160a1f4e4c0916c9390024ec347b6157a45a0992"},
- {file = "yarl-1.24.2-cp313-cp313-win_amd64.whl", hash = "sha256:7d37fb7c38f2b6edab0f845c4f85148d4c44204f52bc127021bd2bc9fdbf1656"},
- {file = "yarl-1.24.2-cp313-cp313-win_arm64.whl", hash = "sha256:1e831894be7c2954240e49791fa4b50c05a0dc881de2552cfe3ffd8631c7f461"},
- {file = "yarl-1.24.2-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:f9312b3c02d9b3d23840f67952913c9c8721d7f1b7db305289faefa878f364c2"},
- {file = "yarl-1.24.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:a4f4d6cd615823bfc7fb7e9b5987c3f41666371d870d51058f77e2680fbe9630"},
- {file = "yarl-1.24.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0c3063e5c0a8e8e62fae6c2596fa01da1561e4cd1da6fec5789f5cf99a8aefd8"},
- {file = "yarl-1.24.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fecd17873a096036c1c87ab3486f1aef7f269ada7f23f7f856f93b1cc7744f14"},
- {file = "yarl-1.24.2-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a46d1ab4ba4d32e6dc80daf8a28ce0bd83d08df52fbc32f3e288663427734535"},
- {file = "yarl-1.24.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:73e68edf6dfd5f73f9ca127d84e2a6f9213c65bdffb736bda19524c0564fcd14"},
- {file = "yarl-1.24.2-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a296ca617f2d25fbceafb962b88750d627e5984e75732c712154d058ae8d79a3"},
- {file = "yarl-1.24.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e51b2cf5ec89a8b8470177641ed62a3ba22d74e1e898e06ad53aa77972487208"},
- {file = "yarl-1.24.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:310fc687f7b2044ec54e372c8cbe923bb88f5c37bded0d3079e5791c2fc3cf50"},
- {file = "yarl-1.24.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:297a2fe352ecf858b30a98f87948746ec16f001d279f84aebdbd3bd965e2f1bd"},
- {file = "yarl-1.24.2-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:2a263e76b97bc42bdcd7c5f4953dec1f7cd62a1112fa7f869e57255229390d67"},
- {file = "yarl-1.24.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:822519b64cf0b474f1a0aaef1dc621438ea46bb77c94df97a5b4d213a7d8a8b1"},
- {file = "yarl-1.24.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:b6067060d9dc594899ba83e6db6c48c68d1e494a6dab158156ed86977ca7bcb1"},
- {file = "yarl-1.24.2-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:0063adad533e57171b79db3943b229d40dfafeeee579767f96541f106bac5f1b"},
- {file = "yarl-1.24.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ee8e3fb34513e8dc082b586ef4910c98335d43a6fab688cd44d4851bacfce3e8"},
- {file = "yarl-1.24.2-cp314-cp314-win_amd64.whl", hash = "sha256:afb00d7fd8e0f285ca29a44cc50df2d622ff2f7a6d933fa641577b5f9d5f3db0"},
- {file = "yarl-1.24.2-cp314-cp314-win_arm64.whl", hash = "sha256:68cf6eacd6028ef1142bc4b48376b81566385ca6f9e7dde3b0fa91be08ffcb57"},
- {file = "yarl-1.24.2-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:221ce1dd921ac4f603957f17d7c18c5cc0797fbb52f156941f92e04605d1d67b"},
- {file = "yarl-1.24.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:5f3224db28173a00d7afacdee07045cc4673dfab2b15492c7ae10deddbece761"},
- {file = "yarl-1.24.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c557165320d6244ebe3a02431b2a201a20080e02f41f0cfa0ccc47a183765da8"},
- {file = "yarl-1.24.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:904065e6e85b1fa54d0d87438bd58c14c0bad97aad654ad1077fd9d87e8478ed"},
- {file = "yarl-1.24.2-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8cec2a38d70edc10e0e856ceda886af5327a017ccbde8e1de1bd44d300357543"},
- {file = "yarl-1.24.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e7484b9361ed222ee1ca5b4337aa4cbdcc4618ce5aff57d9ef1582fd95893fc0"},
- {file = "yarl-1.24.2-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:84f9670b89f34db07f81e53aee83e0b938a3412329d51c8f922488be7fcc4024"},
- {file = "yarl-1.24.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:abb2759733d63a28b4956500a5dd57140f26486c92b2caedfb964ab7d9b79dbf"},
- {file = "yarl-1.24.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:081c2bf54efe03774d0311172bc04fedf9ca01e644d4cd8c805688e527209bdc"},
- {file = "yarl-1.24.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:86746bef442aa479107fe28132e1277237f9c24c2f00b0b0cf22b3ee0904f2bb"},
- {file = "yarl-1.24.2-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:2d07d21d0bc4b17558e8de0b02fbfdf1e347d3bb3699edd00bb92e7c57925420"},
- {file = "yarl-1.24.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:4fb1ac3fc5fecd8ae7453ea237e4d22b49befa70266dfe1629924245c21a0c7f"},
- {file = "yarl-1.24.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:4da31a5512ed1729ca8d8aacde3f7faeb8843cde3165d6bcf7f88f74f17bb8aa"},
- {file = "yarl-1.24.2-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:533ded4dceb5f1f3da7906244f4e82cf46cfd40d84c69a1faf5ac506aa65ecbe"},
- {file = "yarl-1.24.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:7b3a85525f6e7eeabcfdd372862b21ee1915db1b498a04e8bf0e389b607ff0bd"},
- {file = "yarl-1.24.2-cp314-cp314t-win_amd64.whl", hash = "sha256:a7624b1ca46ca5d7b864ef0d2f8efe3091454085ee1855b4e992314529972215"},
- {file = "yarl-1.24.2-cp314-cp314t-win_arm64.whl", hash = "sha256:e434a45ce2e7a947f951fc5a8944c8cc080b7e59f9c50ae80fd39107cf88126d"},
- {file = "yarl-1.24.2-py3-none-any.whl", hash = "sha256:2783d9226db8797636cd6896e4de81feed252d1db72265686c9558d97a4d94b9"},
- {file = "yarl-1.24.2.tar.gz", hash = "sha256:9ac374123c6fd7abf64d1fec93962b0bd4ee2c19751755a762a72dd96c0378f8"},
+ {file = "yarl-1.24.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:88f50c94e21a0a7f14042c015b0eba1881af78562e7bf007e0033e624da59750"},
+ {file = "yarl-1.24.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6efbccc3d7f75d5b03105172a8dc86d82ba4da86817952529dd93185f4a88be2"},
+ {file = "yarl-1.24.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0ebfaffe1a16cb72141c8e09f18cc76856dbe58639f393a4f2b26e474b96b871"},
+ {file = "yarl-1.24.5-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8ac73abdc7ab75610f95a8fd994c6457e87752b02a63987e188f937a1fc180f0"},
+ {file = "yarl-1.24.5-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4d97a951a81039050e45f04e96689b58b8243fa5e62aa14fe67cb6075300885e"},
+ {file = "yarl-1.24.5-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:fe7b7bb170daccbba19ad33012d2b15f1e7942296fd4d45fc1b79013da8cc0f2"},
+ {file = "yarl-1.24.5-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:89a1bbb58e0e3f7a283653d854b1e95d65e5cfd4af224dac5f02629ec1a3e621"},
+ {file = "yarl-1.24.5-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7fa5e51397466ea7e98de493fa2ff1b8193cfef8a7b0f9b4842f92d342df0dba"},
+ {file = "yarl-1.24.5-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4103b77b8a8225e413107d2349b65eb3c1c52627b5cc5c3c4c1c6a798b218950"},
+ {file = "yarl-1.24.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f9f3e9c8a9ecffa57bef8fb4fa19e5fa4d2d8307cf6bac5b1fca5e5860f4ba00"},
+ {file = "yarl-1.24.5-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:c0ebc836c47a6477e182169c6a476fc691d12b518894bf7dd2572f0d59f1c7ed"},
+ {file = "yarl-1.24.5-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:96d30286dd02679e32a39aa8f0b7498fc847fcda46cfc09df5513e82ce252440"},
+ {file = "yarl-1.24.5-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:fd8c81f346b58f45818d09ea11db69a8d5fd34a224b79871f6d44f12cd7977b1"},
+ {file = "yarl-1.24.5-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:5c55256dee8f4b27bfbf636c8363383c7c8db7890c7cba5217d7bd5f5f21dab6"},
+ {file = "yarl-1.24.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9f4d8cf085a4c6a40fb97ea0f46938a8df43c85d31f9d45e2a8867ea9293790d"},
+ {file = "yarl-1.24.5-cp310-cp310-win_amd64.whl", hash = "sha256:240cbec09667c1fed4c6cd0060b9ec57332427d7441289a2ed8875dc9fb2b224"},
+ {file = "yarl-1.24.5-cp310-cp310-win_arm64.whl", hash = "sha256:8a6987eaad834cb32dd57d9d582225f0054a5d1af706ccfbbdba735af4927e13"},
+ {file = "yarl-1.24.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2c1fe720934a16ea8e7146175cba2126f87f54912c8c5435e7f7c7a51ef808d3"},
+ {file = "yarl-1.24.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c687ed078e145f5fd53a14854beff320e1d2ab76df03e2009c98f39a0f68f39a"},
+ {file = "yarl-1.24.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:709f1efed56c4a145793c046cd4939f9959bcd818979a787b77d8e09c57a0840"},
+ {file = "yarl-1.24.5-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:874019bd513008b009f58657134e5d0c5e030b3559bd0553976837adf52fe966"},
+ {file = "yarl-1.24.5-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a4582acf7ef76482f6f511ebaf1946dae7f2e85ec4728b81a678c01df63bd723"},
+ {file = "yarl-1.24.5-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2cabe6546e41dabe439999a23fcb5246e0c3b595b4315b96ef755252be90caeb"},
+ {file = "yarl-1.24.5-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:17f57620f5475b3c69109376cc87e42a7af5db13c9398e4292772a706ff10780"},
+ {file = "yarl-1.24.5-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:570fec8fbd22b032733625f03f10b7ff023bc399213db15e72a7acaef28c2f4e"},
+ {file = "yarl-1.24.5-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5fede79c6f73ff2c3ef822864cb1ada23196e62756df53bc6231d351a49516a2"},
+ {file = "yarl-1.24.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8ccf9aca873b767977c73df497a85dbedee4ee086ae9ae49dc461333b9b79f58"},
+ {file = "yarl-1.24.5-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:ad5d8201d310b031e6cd839d9bac2d4e5a01533ce5d3d5b50b7de1ef3af1de61"},
+ {file = "yarl-1.24.5-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:841f0852f48fefea3b12c9dfec00704dfa3aef5215d0e3ce564bb3d7cd8d57c6"},
+ {file = "yarl-1.24.5-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:9baafc71b04f8f4bb0703b21d6fc9f0c30b346c636a532ff16ec8491a5ea4b1f"},
+ {file = "yarl-1.24.5-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:d897129df1a22b12aeed2c2c98df0785a2e8e6e0bde87b389491d0025c187077"},
+ {file = "yarl-1.24.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:dd625535328fd9882374356269227670189adfcc6a2d90284f323c05862eecbd"},
+ {file = "yarl-1.24.5-cp311-cp311-win_amd64.whl", hash = "sha256:f4239bbec5a3577ddb49e4b50aeb32d8e5792098262ae2f63723f916a29b1a25"},
+ {file = "yarl-1.24.5-cp311-cp311-win_arm64.whl", hash = "sha256:3ac6aff147deb9c09461b2d4bbdf6256831198f5d8a23f5d37138213090b6d8a"},
+ {file = "yarl-1.24.5-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d693396e5aea78db03decd60aec9ece16c9b40ba00a587f089615ff4e718a81d"},
+ {file = "yarl-1.24.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3363fcc96e665878946ad7a106b9a13eac0541766a690ef287c0232ac768b6ec"},
+ {file = "yarl-1.24.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9d399bdcfb4a0f659b9b3788bbc89babe63d9a6a65aacdf4d4e7065ff2e6316c"},
+ {file = "yarl-1.24.5-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:90333fd89b43c0d08ac85f3f1447593fc2c66de18c3d6378d7125ea118dc7a54"},
+ {file = "yarl-1.24.5-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:665b0a2c463cc9423dd647e0bfd9f4ccc9b50f768c55304d5e9f80b177c1de12"},
+ {file = "yarl-1.24.5-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e006d3a974c4ee19512e5f058abedb6eef36a5e553c14812bdeba1758d812e6d"},
+ {file = "yarl-1.24.5-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e7d42c531243450ef0d4d9c172e7ed6ef052640f195629065041b5add4e058d1"},
+ {file = "yarl-1.24.5-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f08c7513ecef5aad65687bfdf6bc601ae9fccd04a42904501f8f7141abad9eb9"},
+ {file = "yarl-1.24.5-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6c95b17fe34ed802f17e205112e6e10db92275c34fee290aa9bdc55a9c724027"},
+ {file = "yarl-1.24.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:56b149b22de33b23b0c6077ab9518c6dcb538ad462e1830e68d06591ccf6e38b"},
+ {file = "yarl-1.24.5-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:a8fe66b8f300da93798025a785a5b90b42f3810dc2b72283ff84a41aaaebc293"},
+ {file = "yarl-1.24.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:377fe3732edbaf78ee74efdf2c9f49f6e99f20e7f9d2649fda3eb4badd77d76e"},
+ {file = "yarl-1.24.5-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:e8ffa78582120024f476a611d7befc123cee59e47e8309d470cf667d806e613b"},
+ {file = "yarl-1.24.5-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:daba5e594f06114e37db186efd2dd916609071e59daca901a0a2e71f02b142ce"},
+ {file = "yarl-1.24.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:65be18ec59496c13908f02a2472751d9ef840b4f3fb5726f129306bf6a2a7bba"},
+ {file = "yarl-1.24.5-cp312-cp312-win_amd64.whl", hash = "sha256:a929d878fec099030c292803b31e5d5540a7b6a31e6a3cc76cb4685fc2a2f51b"},
+ {file = "yarl-1.24.5-cp312-cp312-win_arm64.whl", hash = "sha256:7ce27823052e2013b597e0c738b13e7e36b8ccb9400df8959417b052ab0fd92c"},
+ {file = "yarl-1.24.5-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:79af890482fc94648e8cde4c68620378f7fef60932710fa17a66abc039244da2"},
+ {file = "yarl-1.24.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:46c2f213e23a04b93a392942d782eb9e413e6ef6bf7c8c53884e599a5c174dcb"},
+ {file = "yarl-1.24.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:92ab3e11448f2ff7bf53c5a26eff0edc086898ec8b21fb154b85839ce1d88075"},
+ {file = "yarl-1.24.5-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ebb0ec7f17803063d5aeb982f3b1bd2b2f4e4fae6751226cbd6ba1fcfe9e63ff"},
+ {file = "yarl-1.24.5-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:82632daed195dcc8ea664e8556dc9bdbd671960fb3776bd92806ce05792c2448"},
+ {file = "yarl-1.24.5-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:53e549287ef628fecba270045c9701b0c564563a9b0577d24a4ec75b8ab8040f"},
+ {file = "yarl-1.24.5-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fcd3b77e2f17bbe4ca56ec7bcb07992647d19d0b9c05d84886dcd6f9eb810afd"},
+ {file = "yarl-1.24.5-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d46b86567dd4e248c6c159fcbcdcce01e0a5c8a7cd2334a0fff759d0fa075b16"},
+ {file = "yarl-1.24.5-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7f72c74aa99359e27a2ee8d6613fefa28b5f76a983c083074dfc2aaa4ab46213"},
+ {file = "yarl-1.24.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3f45789ce415a7ec0820dc4f82925f9b5f7732070be1dec1f5f23ec381435a24"},
+ {file = "yarl-1.24.5-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:6e73e7fe93f17a7b191f52ec9da9dd8c06a8fe735a1ecbd13b97d1c723bff385"},
+ {file = "yarl-1.24.5-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4a36f9becdd4c5c52a20c3e9484128b070b1dcfc8944c006f3a528295a359a9c"},
+ {file = "yarl-1.24.5-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:7bcbe0fcf850eae67b6b01749815a4f7161c560a844c769ad7b48fcd99f791c4"},
+ {file = "yarl-1.24.5-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:24e861e9630e0daddcb9191fb187f60f034e17a4426f8101279f0c475cd74144"},
+ {file = "yarl-1.24.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9335a099ad87287c37fe5d1a982ff392fa5efe5d14b40a730b1ec1d6a41382b4"},
+ {file = "yarl-1.24.5-cp313-cp313-win_amd64.whl", hash = "sha256:2dbe06fc16bc91502bca713704022182e5729861ae00277c3a23354b40929740"},
+ {file = "yarl-1.24.5-cp313-cp313-win_arm64.whl", hash = "sha256:6b8536851f9f65e7f00c7a1d49ba7f2be0ffe2c11555367fc9f50d9f842410a1"},
+ {file = "yarl-1.24.5-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:2729fcfc4f6a596fb0c50f32090400aa9367774ac296a00387e65098c0befa76"},
+ {file = "yarl-1.24.5-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:ff330d3c30db4eb6b01d79e29d2d0b407a7ecad39cfd9ec993ece57396a2ec0d"},
+ {file = "yarl-1.24.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:e42d75862735da90e7fc5a7b23db0c976f737113a54b3c9777a9b665e9cbff75"},
+ {file = "yarl-1.24.5-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a3732e66413163e72508da9eff9ce9d2846fde51fae45d3605393d3e6cd303e9"},
+ {file = "yarl-1.24.5-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5b8ee53be440a0cffc991a27be3057e0530122548dbe7c0892df08822fce5ede"},
+ {file = "yarl-1.24.5-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:af3aefa655adb5869491fa907e652290386800ae99cc50095cba71e2c6aefdca"},
+ {file = "yarl-1.24.5-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2120b96872df4a117cde97d270bac96aea7cc52205d305cf4611df694a487027"},
+ {file = "yarl-1.24.5-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:66410eb6345d467151934b49bfa70fb32f5b35a6140baa40ad97d6436abea2e9"},
+ {file = "yarl-1.24.5-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4af7b7e1be0a69bee8210735fe6dcfc38879adfac6d62e789d53ba432d1ffa41"},
+ {file = "yarl-1.24.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fa139875ff98ab97da323cfadfaff08900d1ad42f1b5087b0b812a55c5a06373"},
+ {file = "yarl-1.24.5-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:0055afc45e864b92729ac7600e2d102c17bef060647e74bca75fa84d66b9ff36"},
+ {file = "yarl-1.24.5-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:f0e466ed7511fe9d459a819edbc6c2585c0b6eabde9fa8a8947552468a7a6ef0"},
+ {file = "yarl-1.24.5-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:f141474e85b7e54998ec5180530a7cda99ab29e282fa50e0756d89981a9b43c5"},
+ {file = "yarl-1.24.5-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:e2935f8c39e3b03e83519292d78f075189978f3f4adc15a78144c7c8e2a1cba5"},
+ {file = "yarl-1.24.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9d1216a7f6f77836617dba35687c5b78a4170afc3c3f18fc788f785ba26565c4"},
+ {file = "yarl-1.24.5-cp314-cp314-win_amd64.whl", hash = "sha256:5ba4f78df2bcc19f764a4b26a8a4f5049c110090ad5825993aacb052bf8003ad"},
+ {file = "yarl-1.24.5-cp314-cp314-win_arm64.whl", hash = "sha256:9e4e16c73d717c5cf27626c524d0a2e261ad20e46932b2670f64ad5dde23e26f"},
+ {file = "yarl-1.24.5-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:e1ae548a9d901adca07899a4147a7c826bbcc06239d3ce9a59f57886a28a4c88"},
+ {file = "yarl-1.24.5-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:ff405d91509d88e8d44129cd87b18d70acd1f0c1aeabd7bc3c46792b1fe2acba"},
+ {file = "yarl-1.24.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:47e98aab9d8d82ff682e7b0b5dded33bf138a32b817fcf7fa3b27b2d7c412928"},
+ {file = "yarl-1.24.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f0a658a6d3fafee5c6f63c58f3e785c8c43c93fbc02bf9f2b6663f8185e0971f"},
+ {file = "yarl-1.24.5-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4377407001ca3c057773f44d8ddd6358fa5f691407c1ba92210bd3cf8d9e4c95"},
+ {file = "yarl-1.24.5-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7c0494a31a1ac5461a226e7947a9c9b78c44e1dc7185164fa7e9651557a5d9bc"},
+ {file = "yarl-1.24.5-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a7cff474ab7cd149765bb784cf6d78b32e18e20473fb7bda860bce98ab58e9da"},
+ {file = "yarl-1.24.5-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cbb833ccacdb5519eff9b8b71ee618cc2801c878e77e288775d77c3a2ced858a"},
+ {file = "yarl-1.24.5-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:82f75e05912e84b7a0fe57075d9c59de3cb352b928330f2eb69b2e1f54c3e1f0"},
+ {file = "yarl-1.24.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:16a2f5010280020e90f5330257e6944bc33e73593b136cc5a241e6c1dc292498"},
+ {file = "yarl-1.24.5-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:ffcd54362564dc1a30fb74d8b8a6e5a6b11ebd5e27266adc3b7427a21a6c9104"},
+ {file = "yarl-1.24.5-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0465ec8cedc2349b97a6b595ace64084a50c6e839eca40aa0626f38b8350e331"},
+ {file = "yarl-1.24.5-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:4db9aecb141cb7a5447171b57aa1ed3a8fee06af40b992ffc31206c0b0121550"},
+ {file = "yarl-1.24.5-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:f540c013589084679a6c7fac07096b10159737918174f5dfc5e11bf5bca4dfe6"},
+ {file = "yarl-1.24.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a61834fb15d81322d872eaafd333838ae7c9cea84067f232656f75965933d047"},
+ {file = "yarl-1.24.5-cp314-cp314t-win_amd64.whl", hash = "sha256:5c88e5815a49d289e599f3513aa7fde0bc2092ff188f99c940f007f90f53d104"},
+ {file = "yarl-1.24.5-cp314-cp314t-win_arm64.whl", hash = "sha256:cf139c02f5f23ef6532040a30ff662c00a318c952334f211046b8e60b7f17688"},
+ {file = "yarl-1.24.5-py3-none-any.whl", hash = "sha256:a33700d13d9b7d84fd10947b09ff69fb9a792e519c8cb9764a3ca70baa6c23a7"},
+ {file = "yarl-1.24.5.tar.gz", hash = "sha256:e81b83143bee16329c23db3c1b2d82b29892fcbcb849186d2f6e98a5abe9a57f"},
]
[package.dependencies]
@@ -1410,6 +1464,6 @@ propcache = ">=0.2.1"
aiohttp = ["aiohttp", "httpx-aiohttp"]
[metadata]
-lock-version = "2.0"
+lock-version = "2.1"
python-versions = "^3.10"
-content-hash = "747cf6806d0d2a32dda32c0f4beb23d48047dee5fab394a6dbe55935c60b949a"
+content-hash = "4eaeff06fb8e558535aa3729ed6f99c2398eaaba665bcdf9c096bb08d9cb5fc6"
diff --git a/pyproject.toml b/pyproject.toml
index 909380b..613303e 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,7 +4,7 @@ dynamic = ["version"]
[tool.poetry]
name = "junction-api-sdk"
-version = "1.3.0"
+version = "1.4.0"
description = ""
readme = "README.md"
authors = []
@@ -37,9 +37,9 @@ Repository = 'https://github.com/junction-api/junction-python'
[tool.poetry.dependencies]
python = "^3.10"
-aiohttp = { version = ">=3.13.4,<4", optional = true, python = ">=3.9"}
+aiohttp = { version = ">=3.14.1,<4", optional = true, python = ">=3.10"}
httpx = ">=0.21.2"
-httpx-aiohttp = { version = "0.1.8", optional = true, python = ">=3.9"}
+httpx-aiohttp = { version = "^0.1.8", optional = true, python = ">=3.10"}
pydantic = ">= 1.9.2"
pydantic-core = ">=2.18.2,<3.0.0"
typing_extensions = ">= 4.0.0"
@@ -57,6 +57,7 @@ ruff = "==0.11.5"
[tool.pytest.ini_options]
testpaths = [ "tests" ]
asyncio_mode = "auto"
+norecursedirs = [ "src" ]
markers = [
"aiohttp: tests that require httpx_aiohttp to be installed",
]
diff --git a/reference.md b/reference.md
index 56f34f1..cb8b7ec 100644
--- a/reference.md
+++ b/reference.md
@@ -1213,7 +1213,7 @@ client.electrocardiogram.get(
-
-**start_date:** `str`
+**start_date:** `str` — Start date in YYYY-MM-DD
@@ -1221,7 +1221,7 @@ client.electrocardiogram.get(
-
-**end_date:** `typing.Optional[str]`
+**end_date:** `typing.Optional[str]` — End date (inclusive) in YYYY-MM-DD
@@ -1314,7 +1314,7 @@ client.sleep_cycle.get(
-
-**start_date:** `str`
+**start_date:** `str` — Start date in YYYY-MM-DD
@@ -1322,7 +1322,7 @@ client.sleep_cycle.get(
-
-**end_date:** `typing.Optional[str]`
+**end_date:** `typing.Optional[str]` — End date (inclusive) in YYYY-MM-DD
@@ -2686,7 +2686,7 @@ client.menstrual_cycle.get(
-
-**start_date:** `str`
+**start_date:** `str` — Start date in YYYY-MM-DD
@@ -2702,7 +2702,7 @@ client.menstrual_cycle.get(
-
-**end_date:** `typing.Optional[str]`
+**end_date:** `typing.Optional[str]` — End date (inclusive) in YYYY-MM-DD
@@ -13013,6 +13013,7 @@ client.lab_tests.get_markers(
lab_slug="lab_slug",
name="name",
a_la_carte_enabled=True,
+ include_pricing=True,
lab_account_id="lab_account_id",
page=1,
size=1,
@@ -13064,6 +13065,14 @@ client.lab_tests.get_markers(
-
+**include_pricing:** `typing.Optional[bool]`
+
+
+
+
+
+-
+
**lab_account_id:** `typing.Optional[str]` — The lab account ID. This lab account is used to determine the availability of markers and lab tests.
@@ -13469,6 +13478,8 @@ client = Junction(
client.lab_tests.get_paginated(
lab_test_limit=1,
next_cursor="next_cursor",
+ include_pricing=True,
+ lab_account_id="lab_account_id",
generation_method=LabTestGenerationMethodFilter.AUTO,
lab_slug="lab_slug",
collection_method=LabTestCollectionMethod.TESTKIT,
@@ -13506,7 +13517,23 @@ client.lab_tests.get_paginated(
-
-**next_cursor:** `typing.Optional[str]`
+**next_cursor:** `typing.Optional[str]` — The cursor for fetching the next page, or `null` to fetch the first page.
+
+
+
+
+
+-
+
+**include_pricing:** `typing.Optional[bool]`
+
+
+
+
+
+-
+
+**lab_account_id:** `typing.Optional[str]` — The lab account ID. This lab account is used to determine the availability of markers and lab tests.
@@ -16538,7 +16565,7 @@ client.lab_tests.update_on_site_collection_order_draw_completed(
-client.lab_tests.validate_icd_codes(...) -> ValidateIcdCodesResponse
+client.lab_tests.list_unmatched_result_test_cases() -> ListUnmatchedResultTestCasesResponse
-
@@ -16559,10 +16586,59 @@ client = Junction(
environment=JunctionEnvironment.PRODUCTION,
)
-client.lab_tests.validate_icd_codes(
- codes=[
- "codes"
- ],
+client.lab_tests.list_unmatched_result_test_cases()
+
+```
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration.
+
+
+
+
+
+
+
+
+
+
+
+client.lab_tests.create_unmatched_result_test(...) -> CreateUnmatchedResultTestResponse
+
+-
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```python
+from junction import Junction, UnmatchedResultTestCase, UnmatchedResultTestOrderSource
+from junction.environment import JunctionEnvironment
+
+client = Junction(
+ api_key="",
+ environment=JunctionEnvironment.PRODUCTION,
+)
+
+client.lab_tests.create_unmatched_result_test(
+ idempotency_key="X-Idempotency-Key",
+ case=UnmatchedResultTestCase.MATCH_COMPLETED,
+ order_source=UnmatchedResultTestOrderSource.MANAGED,
)
```
@@ -16579,7 +16655,63 @@ client.lab_tests.validate_icd_codes(
-
-**codes:** `typing.List[str]`
+**idempotency_key:** `str`
+
+
+
+
+
+-
+
+**case:** `UnmatchedResultTestCase` — ℹ️ This enum is non-exhaustive.
+
+
+
+
+
+-
+
+**order_source:** `UnmatchedResultTestOrderSource` — ℹ️ This enum is non-exhaustive.
+
+
+
+
+
+-
+
+**orders:** `typing.Optional[typing.Dict[str, typing.Optional[str]]]`
+
+
+
+
+
+-
+
+**result_status:** `typing.Optional[ResultStatus]` — ℹ️ This enum is non-exhaustive.
+
+
+
+
+
+-
+
+**interpretation:** `typing.Optional[Interpretation]` — ℹ️ This enum is non-exhaustive.
+
+
+
+
+
+-
+
+**lab_test_id:** `typing.Optional[str]`
+
+
+
+
+
+-
+
+**wrong_lab_test_id:** `typing.Optional[str]`
@@ -16599,8 +16731,7 @@ client.lab_tests.validate_icd_codes(
-## Compendium
-client.compendium.search(...) -> SearchCompendiumResponse
+client.lab_tests.get_unmatched_result_test(...) -> GetUnmatchedResultTestResponse
-
@@ -16613,7 +16744,7 @@ client.lab_tests.validate_icd_codes(
-
```python
-from junction import Junction, SearchMode
+from junction import Junction
from junction.environment import JunctionEnvironment
client = Junction(
@@ -16621,8 +16752,8 @@ client = Junction(
environment=JunctionEnvironment.PRODUCTION,
)
-client.compendium.search(
- mode=SearchMode.CANONICAL,
+client.lab_tests.get_unmatched_result_test(
+ run_id="run_id",
)
```
@@ -16639,7 +16770,7 @@ client.compendium.search(
-
-**mode:** `SearchMode` — ℹ️ This enum is non-exhaustive.
+**run_id:** `str`
@@ -16647,15 +16778,65 @@ client.compendium.search(
-
-**query:** `typing.Optional[str]`
+**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration.
+
+
+
+
+
+
+
+client.lab_tests.list_unmatched_results(...) -> ListUnmatchedResultResponse
-
-**cpt_codes:** `typing.Optional[typing.List[str]]`
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```python
+from junction import Junction, MatchDecisionCode, MatchReviewStatusFilter
+from junction.environment import JunctionEnvironment
+
+client = Junction(
+ api_key="",
+ environment=JunctionEnvironment.PRODUCTION,
+)
+
+client.lab_tests.list_unmatched_results(
+ limit=1,
+ next_cursor="next_cursor",
+ decision_code=MatchDecisionCode.MATCH_SAMPLE_ID,
+ lab_slug="lab_slug",
+ status=MatchReviewStatusFilter.PENDING_CUSTOMER_REVIEW,
+ created_at_start="created_at_start",
+ created_at_end="created_at_end",
+ search_input="search_input",
+)
+
+```
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**limit:** `typing.Optional[int]`
@@ -16663,7 +16844,7 @@ client.compendium.search(
-
-**loinc_set_hash:** `typing.Optional[str]`
+**next_cursor:** `typing.Optional[str]` — The cursor for fetching the next page, or `null` to fetch the first page.
@@ -16671,7 +16852,7 @@ client.compendium.search(
-
-**labs:** `typing.Optional[typing.List[CompendiumSearchLabs]]`
+**decision_code:** `typing.Optional[MatchDecisionCode]` — Filter by match decision code.
@@ -16679,7 +16860,7 @@ client.compendium.search(
-
-**include_related:** `typing.Optional[bool]`
+**lab_slug:** `typing.Optional[str]` — Filter by lab slug (e.g. `labcorp`, `quest`).
@@ -16687,7 +16868,31 @@ client.compendium.search(
-
-**limit:** `typing.Optional[int]`
+**status:** `typing.Optional[MatchReviewStatusFilter]` — Filter by review status. `pending_customer_review` returns items awaiting your action; `pending_ops_review` returns items you have escalated for review.
+
+
+
+
+
+-
+
+**created_at_start:** `typing.Optional[str]` — Filter by result receipt date on or after this date (UTC, inclusive, YYYY-MM-DD).
+
+
+
+
+
+-
+
+**created_at_end:** `typing.Optional[str]` — Filter by result receipt date on or before this date (UTC, inclusive, YYYY-MM-DD).
+
+
+
+
+
+-
+
+**search_input:** `typing.Optional[str]` — Search by patient first name, last name, or date of birth (e.g. `Alice`, `Smith`, or `1990-01-15`).
@@ -16707,7 +16912,7 @@ client.compendium.search(
-client.compendium.convert(...) -> ConvertCompendiumResponse
+client.lab_tests.get_unmatched_result(...) -> GetUnmatchedResultResponse
-
@@ -16720,7 +16925,7 @@ client.compendium.search(
-
```python
-from junction import Junction, CompendiumSearchLabs
+from junction import Junction
from junction.environment import JunctionEnvironment
client = Junction(
@@ -16728,8 +16933,8 @@ client = Junction(
environment=JunctionEnvironment.PRODUCTION,
)
-client.compendium.convert(
- target_lab=CompendiumSearchLabs.LABCORP,
+client.lab_tests.get_unmatched_result(
+ raw_result_id="raw_result_id",
)
```
@@ -16746,7 +16951,7 @@ client.compendium.convert(
-
-**target_lab:** `CompendiumSearchLabs` — ℹ️ This enum is non-exhaustive.
+**raw_result_id:** `str`
@@ -16754,15 +16959,402 @@ client.compendium.convert(
-
-**lab_test_id:** `typing.Optional[str]`
+**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration.
+
+
+
+
+
+
+
+client.lab_tests.accept_unmatched_result(...) -> ClientFacingOrder
-
-**provider_ids:** `typing.Optional[typing.List[str]]`
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```python
+from junction import Junction
+from junction.environment import JunctionEnvironment
+
+client = Junction(
+ api_key="",
+ environment=JunctionEnvironment.PRODUCTION,
+)
+
+client.lab_tests.accept_unmatched_result(
+ raw_result_id="raw_result_id",
+)
+
+```
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**raw_result_id:** `str`
+
+
+
+
+
+-
+
+**user_id:** `typing.Optional[str]`
+
+
+
+
+
+-
+
+**order_id:** `typing.Optional[str]`
+
+
+
+
+
+-
+
+**note:** `typing.Optional[str]`
+
+
+
+
+
+-
+
+**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration.
+
+
+
+
+
+
+
+
+
+
+
+client.lab_tests.resolve_unmatched_result(...) -> UnmatchedResult
+
+-
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```python
+from junction import Junction, UnmatchedResultResolutionAction
+from junction.environment import JunctionEnvironment
+
+client = Junction(
+ api_key="",
+ environment=JunctionEnvironment.PRODUCTION,
+)
+
+client.lab_tests.resolve_unmatched_result(
+ raw_result_id="raw_result_id",
+ action=UnmatchedResultResolutionAction.REJECT,
+)
+
+```
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**raw_result_id:** `str`
+
+
+
+
+
+-
+
+**action:** `UnmatchedResultResolutionAction` — ℹ️ This enum is non-exhaustive.
+
+
+
+
+
+-
+
+**note:** `typing.Optional[str]`
+
+
+
+
+
+-
+
+**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration.
+
+
+
+
+
+
+
+
+
+
+
+client.lab_tests.validate_icd_codes(...) -> ValidateIcdCodesResponse
+
+-
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```python
+from junction import Junction
+from junction.environment import JunctionEnvironment
+
+client = Junction(
+ api_key="",
+ environment=JunctionEnvironment.PRODUCTION,
+)
+
+client.lab_tests.validate_icd_codes(
+ codes=[
+ "codes"
+ ],
+)
+
+```
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**codes:** `typing.List[str]`
+
+
+
+
+
+-
+
+**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration.
+
+
+
+
+
+
+
+
+
+
+
+## Compendium
+client.compendium.search(...) -> SearchCompendiumResponse
+
+-
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```python
+from junction import Junction, SearchMode
+from junction.environment import JunctionEnvironment
+
+client = Junction(
+ api_key="",
+ environment=JunctionEnvironment.PRODUCTION,
+)
+
+client.compendium.search(
+ mode=SearchMode.CANONICAL,
+)
+
+```
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**mode:** `SearchMode` — ℹ️ This enum is non-exhaustive.
+
+
+
+
+
+-
+
+**query:** `typing.Optional[str]`
+
+
+
+
+
+-
+
+**cpt_codes:** `typing.Optional[typing.List[str]]`
+
+
+
+
+
+-
+
+**loinc_set_hash:** `typing.Optional[str]`
+
+
+
+
+
+-
+
+**labs:** `typing.Optional[typing.List[CompendiumSearchLabs]]`
+
+
+
+
+
+-
+
+**include_related:** `typing.Optional[bool]`
+
+
+
+
+
+-
+
+**limit:** `typing.Optional[int]`
+
+
+
+
+
+-
+
+**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration.
+
+
+
+
+
+
+
+
+
+
+
+client.compendium.convert(...) -> ConvertCompendiumResponse
+
+-
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```python
+from junction import Junction, CompendiumSearchLabs
+from junction.environment import JunctionEnvironment
+
+client = Junction(
+ api_key="",
+ environment=JunctionEnvironment.PRODUCTION,
+)
+
+client.compendium.convert(
+ target_lab=CompendiumSearchLabs.LABCORP,
+)
+
+```
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**target_lab:** `CompendiumSearchLabs` — ℹ️ This enum is non-exhaustive.
+
+
+
+
+
+-
+
+**lab_test_id:** `typing.Optional[str]`
+
+
+
+
+
+-
+
+**provider_ids:** `typing.Optional[typing.List[str]]`
@@ -16786,6 +17378,84 @@ client.compendium.convert(
+
+
+
+
+client.compendium.search_orderable_tests(...) -> SearchOrderableTestsResponse
+
+-
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```python
+from junction import Junction, CompendiumSearchLabs
+from junction.environment import JunctionEnvironment
+
+client = Junction(
+ api_key="",
+ environment=JunctionEnvironment.PRODUCTION,
+)
+
+client.compendium.search_orderable_tests(
+ provider_ids=[
+ "provider_ids"
+ ],
+ target_lab=CompendiumSearchLabs.LABCORP,
+)
+
+```
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**provider_ids:** `typing.List[str]`
+
+
+
+
+
+-
+
+**target_lab:** `CompendiumSearchLabs` — ℹ️ This enum is non-exhaustive.
+
+
+
+
+
+-
+
+**per_provider_limit:** `typing.Optional[int]`
+
+
+
+
+
+-
+
+**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration.
+
+
+
+
+
+
+
@@ -17116,7 +17786,7 @@ client.testkit.register(
-
-**user_id:** `typing.Optional[str]` — The user ID of the patient.
+**user_id:** `typing.Optional[str]` — The user ID of the patient. If it differs from the user currently associated with the unregistered testkit order, the order is rebound to this user at registration time. The user must exist on the same team as the order. If omitted, the order's existing user is kept.
@@ -17856,7 +18526,6 @@ client = Junction(
client.aggregate.query_one(
user_id="user_id",
- accept="*/*",
timeframe=RelativeTimeframe(
type="relative",
anchor="anchor",
@@ -17968,7 +18637,6 @@ client = Junction(
client.aggregate.get_result_table_for_continuous_query(
user_id="user_id",
query_id_or_slug="query_id_or_slug",
- accept="*/*",
)
```
diff --git a/src/junction/__init__.py b/src/junction/__init__.py
index 6659632..e1888bb 100644
--- a/src/junction/__init__.py
+++ b/src/junction/__init__.py
@@ -247,6 +247,8 @@
ClientFacingLoinc,
ClientFacingMarker,
ClientFacingMarkerComplete,
+ ClientFacingMatchReviewChanged,
+ ClientFacingMatchReviewUpdated,
ClientFacingMealChanged,
ClientFacingMealChangedEventType,
ClientFacingMealHistoricalPullCompleted,
@@ -426,6 +428,7 @@
ContraceptiveEntry,
ContraceptiveEntryType,
ConvertCompendiumResponse,
+ CreateUnmatchedResultTestResponse,
CreateUserPortalUrlResponse,
DatePartExpr,
DatePartExprArg,
@@ -456,11 +459,14 @@
Fats,
Gender,
GenderIdentity,
+ GetLabTestPricingResponse,
GetMarkersResponse,
GetOrderCommunicationSettingsResponse,
GetOrderTransactionResponse,
GetOrdersResponse,
GetTeamLabAccountsResponse,
+ GetUnmatchedResultResponse,
+ GetUnmatchedResultTestResponse,
GroupKeyColumnExpr,
GroupKeyColumnExprGroupKey,
GroupedAFibBurden,
@@ -607,6 +613,9 @@
LabResultsRawResults,
LabTestCollectionMethod,
LabTestGenerationMethodFilter,
+ LabTestPanelPricing,
+ LabTestPanelPricingMarkerBreakdownValue,
+ LabTestPanelPricingPricing,
LabTestResourcesResponse,
LabTestSampleType,
LabTestStatus,
@@ -614,10 +623,29 @@
LastAttempt,
LibreConfig,
LinkTokenExchangeResponse,
+ ListUnmatchedResultResponse,
+ ListUnmatchedResultTestCasesResponse,
LngLat,
LoincMatch,
Macros,
+ MarkerPricingConditions,
+ MarkerPricingResponse,
+ MarkerPricingResponseDataValue,
MarkerType,
+ MatchCandidateConfidenceLevel,
+ MatchCandidateReasonCode,
+ MatchCandidateType,
+ MatchDecisionCode,
+ MatchReviewCandidate,
+ MatchReviewCandidateGroup,
+ MatchReviewLab,
+ MatchReviewMarker,
+ MatchReviewPatient,
+ MatchReviewResolutionAction,
+ MatchReviewStatus,
+ MatchReviewStatusFilter,
+ MatchReviewWebhookPayload,
+ MatchSubReasonCode,
McBasalBodyTemperatureFieldExpr,
McBasalBodyTemperatureFieldExprBasalBodyTemperature,
McCervicalMucusFieldExpr,
@@ -690,6 +718,9 @@
Placeholder,
Png,
PostOrderResponse,
+ PricingModifierMarkerPricingConditions,
+ PricingModifierMarkerPricingConditionsDeltaAmountMinor,
+ PricingModifierRange,
PriorityResource,
ProfileColumnExpr,
ProfileColumnExprProfile,
@@ -726,6 +757,7 @@
RawSleepResponse,
RawWorkout,
RawWorkoutResponse,
+ ReflexPricingCondition,
Region,
RelatedCandidate,
RelativeTimeframe,
@@ -734,6 +766,7 @@
ResponsibleRelationship,
ResultMetadata,
ResultMetadataGender,
+ ResultStatus,
ResultType,
SampleData,
SampleDataDateCollected,
@@ -746,6 +779,7 @@
ScopeRequirementsStr,
SearchCompendiumResponse,
SearchMode,
+ SearchOrderableTestsResponse,
Select,
Sex,
SexualActivityEntry,
@@ -770,6 +804,7 @@
SourceColumnExprSource,
SourceLink,
SourceType,
+ SpecifiedPricingMarkerPricingConditions,
TeamConfig,
TemperatureTimeseriesExpr,
TemperatureTimeseriesExprField,
@@ -778,11 +813,23 @@
TimeseriesMetricPoint,
TimeseriesResource,
TraceElements,
+ UnmatchedResult,
+ UnmatchedResultResolutionAction,
+ UnmatchedResultTestCase,
+ UnmatchedResultTestCaseDefinition,
+ UnmatchedResultTestFailure,
+ UnmatchedResultTestOrderArtifact,
+ UnmatchedResultTestOrderSource,
+ UnmatchedResultTestRunStatus,
+ UnmatchedResultTestStage,
UnnestExpr,
UnnestExprUnnest,
UnrecognizedValueMacroExpr,
+ UnspecifiedPricing,
+ UnspecifiedPricingUnspecified,
UsAddress,
UsState,
+ UsStatePricingCondition,
UserAddress,
UserHistoricalPullsResponse,
UserInfo,
@@ -1091,6 +1138,8 @@
"ClientFacingLoinc": ".types",
"ClientFacingMarker": ".types",
"ClientFacingMarkerComplete": ".types",
+ "ClientFacingMatchReviewChanged": ".types",
+ "ClientFacingMatchReviewUpdated": ".types",
"ClientFacingMealChanged": ".types",
"ClientFacingMealChangedEventType": ".types",
"ClientFacingMealHistoricalPullCompleted": ".types",
@@ -1270,6 +1319,7 @@
"ContraceptiveEntry": ".types",
"ContraceptiveEntryType": ".types",
"ConvertCompendiumResponse": ".types",
+ "CreateUnmatchedResultTestResponse": ".types",
"CreateUserPortalUrlBodyContext": ".user",
"CreateUserPortalUrlResponse": ".types",
"DatePartExpr": ".types",
@@ -1303,6 +1353,7 @@
"Fats": ".types",
"Gender": ".types",
"GenderIdentity": ".types",
+ "GetLabTestPricingResponse": ".types",
"GetLabTestsRequestOrderDirection": ".lab_tests",
"GetLabTestsRequestOrderKey": ".lab_tests",
"GetMarkersResponse": ".types",
@@ -1314,6 +1365,8 @@
"GetPaginatedLabTestsRequestOrderDirection": ".lab_tests",
"GetPaginatedLabTestsRequestOrderKey": ".lab_tests",
"GetTeamLabAccountsResponse": ".types",
+ "GetUnmatchedResultResponse": ".types",
+ "GetUnmatchedResultTestResponse": ".types",
"GroupKeyColumnExpr": ".types",
"GroupKeyColumnExprGroupKey": ".types",
"GroupedAFibBurden": ".types",
@@ -1462,6 +1515,9 @@
"LabResultsRawResults": ".types",
"LabTestCollectionMethod": ".types",
"LabTestGenerationMethodFilter": ".types",
+ "LabTestPanelPricing": ".types",
+ "LabTestPanelPricingMarkerBreakdownValue": ".types",
+ "LabTestPanelPricingPricing": ".types",
"LabTestResourcesResponse": ".types",
"LabTestSampleType": ".types",
"LabTestStatus": ".types",
@@ -1469,10 +1525,29 @@
"LastAttempt": ".types",
"LibreConfig": ".types",
"LinkTokenExchangeResponse": ".types",
+ "ListUnmatchedResultResponse": ".types",
+ "ListUnmatchedResultTestCasesResponse": ".types",
"LngLat": ".types",
"LoincMatch": ".types",
"Macros": ".types",
+ "MarkerPricingConditions": ".types",
+ "MarkerPricingResponse": ".types",
+ "MarkerPricingResponseDataValue": ".types",
"MarkerType": ".types",
+ "MatchCandidateConfidenceLevel": ".types",
+ "MatchCandidateReasonCode": ".types",
+ "MatchCandidateType": ".types",
+ "MatchDecisionCode": ".types",
+ "MatchReviewCandidate": ".types",
+ "MatchReviewCandidateGroup": ".types",
+ "MatchReviewLab": ".types",
+ "MatchReviewMarker": ".types",
+ "MatchReviewPatient": ".types",
+ "MatchReviewResolutionAction": ".types",
+ "MatchReviewStatus": ".types",
+ "MatchReviewStatusFilter": ".types",
+ "MatchReviewWebhookPayload": ".types",
+ "MatchSubReasonCode": ".types",
"McBasalBodyTemperatureFieldExpr": ".types",
"McBasalBodyTemperatureFieldExprBasalBodyTemperature": ".types",
"McCervicalMucusFieldExpr": ".types",
@@ -1546,6 +1621,9 @@
"Placeholder": ".types",
"Png": ".types",
"PostOrderResponse": ".types",
+ "PricingModifierMarkerPricingConditions": ".types",
+ "PricingModifierMarkerPricingConditionsDeltaAmountMinor": ".types",
+ "PricingModifierRange": ".types",
"PriorityResource": ".types",
"ProfileColumnExpr": ".types",
"ProfileColumnExprProfile": ".types",
@@ -1583,6 +1661,7 @@
"RawSleepResponse": ".types",
"RawWorkout": ".types",
"RawWorkoutResponse": ".types",
+ "ReflexPricingCondition": ".types",
"Region": ".types",
"RelatedCandidate": ".types",
"RelativeTimeframe": ".types",
@@ -1591,6 +1670,7 @@
"ResponsibleRelationship": ".types",
"ResultMetadata": ".types",
"ResultMetadataGender": ".types",
+ "ResultStatus": ".types",
"ResultType": ".types",
"SampleData": ".types",
"SampleDataDateCollected": ".types",
@@ -1603,6 +1683,7 @@
"ScopeRequirementsStr": ".types",
"SearchCompendiumResponse": ".types",
"SearchMode": ".types",
+ "SearchOrderableTestsResponse": ".types",
"Select": ".types",
"Sex": ".types",
"SexualActivityEntry": ".types",
@@ -1627,6 +1708,7 @@
"SourceColumnExprSource": ".types",
"SourceLink": ".types",
"SourceType": ".types",
+ "SpecifiedPricingMarkerPricingConditions": ".types",
"TeamConfig": ".types",
"TemperatureTimeseriesExpr": ".types",
"TemperatureTimeseriesExprField": ".types",
@@ -1635,12 +1717,24 @@
"TimeseriesMetricPoint": ".types",
"TimeseriesResource": ".types",
"TraceElements": ".types",
+ "UnmatchedResult": ".types",
+ "UnmatchedResultResolutionAction": ".types",
+ "UnmatchedResultTestCase": ".types",
+ "UnmatchedResultTestCaseDefinition": ".types",
+ "UnmatchedResultTestFailure": ".types",
+ "UnmatchedResultTestOrderArtifact": ".types",
+ "UnmatchedResultTestOrderSource": ".types",
+ "UnmatchedResultTestRunStatus": ".types",
+ "UnmatchedResultTestStage": ".types",
"UnnestExpr": ".types",
"UnnestExprUnnest": ".types",
"UnprocessableEntityError": ".errors",
"UnrecognizedValueMacroExpr": ".types",
+ "UnspecifiedPricing": ".types",
+ "UnspecifiedPricingUnspecified": ".types",
"UsAddress": ".types",
"UsState": ".types",
+ "UsStatePricingCondition": ".types",
"UserAddress": ".types",
"UserHistoricalPullsResponse": ".types",
"UserInfo": ".types",
@@ -1956,6 +2050,8 @@ def __dir__():
"ClientFacingLoinc",
"ClientFacingMarker",
"ClientFacingMarkerComplete",
+ "ClientFacingMatchReviewChanged",
+ "ClientFacingMatchReviewUpdated",
"ClientFacingMealChanged",
"ClientFacingMealChangedEventType",
"ClientFacingMealHistoricalPullCompleted",
@@ -2135,6 +2231,7 @@ def __dir__():
"ContraceptiveEntry",
"ContraceptiveEntryType",
"ConvertCompendiumResponse",
+ "CreateUnmatchedResultTestResponse",
"CreateUserPortalUrlBodyContext",
"CreateUserPortalUrlResponse",
"DatePartExpr",
@@ -2168,6 +2265,7 @@ def __dir__():
"Fats",
"Gender",
"GenderIdentity",
+ "GetLabTestPricingResponse",
"GetLabTestsRequestOrderDirection",
"GetLabTestsRequestOrderKey",
"GetMarkersResponse",
@@ -2179,6 +2277,8 @@ def __dir__():
"GetPaginatedLabTestsRequestOrderDirection",
"GetPaginatedLabTestsRequestOrderKey",
"GetTeamLabAccountsResponse",
+ "GetUnmatchedResultResponse",
+ "GetUnmatchedResultTestResponse",
"GroupKeyColumnExpr",
"GroupKeyColumnExprGroupKey",
"GroupedAFibBurden",
@@ -2327,6 +2427,9 @@ def __dir__():
"LabResultsRawResults",
"LabTestCollectionMethod",
"LabTestGenerationMethodFilter",
+ "LabTestPanelPricing",
+ "LabTestPanelPricingMarkerBreakdownValue",
+ "LabTestPanelPricingPricing",
"LabTestResourcesResponse",
"LabTestSampleType",
"LabTestStatus",
@@ -2334,10 +2437,29 @@ def __dir__():
"LastAttempt",
"LibreConfig",
"LinkTokenExchangeResponse",
+ "ListUnmatchedResultResponse",
+ "ListUnmatchedResultTestCasesResponse",
"LngLat",
"LoincMatch",
"Macros",
+ "MarkerPricingConditions",
+ "MarkerPricingResponse",
+ "MarkerPricingResponseDataValue",
"MarkerType",
+ "MatchCandidateConfidenceLevel",
+ "MatchCandidateReasonCode",
+ "MatchCandidateType",
+ "MatchDecisionCode",
+ "MatchReviewCandidate",
+ "MatchReviewCandidateGroup",
+ "MatchReviewLab",
+ "MatchReviewMarker",
+ "MatchReviewPatient",
+ "MatchReviewResolutionAction",
+ "MatchReviewStatus",
+ "MatchReviewStatusFilter",
+ "MatchReviewWebhookPayload",
+ "MatchSubReasonCode",
"McBasalBodyTemperatureFieldExpr",
"McBasalBodyTemperatureFieldExprBasalBodyTemperature",
"McCervicalMucusFieldExpr",
@@ -2411,6 +2533,9 @@ def __dir__():
"Placeholder",
"Png",
"PostOrderResponse",
+ "PricingModifierMarkerPricingConditions",
+ "PricingModifierMarkerPricingConditionsDeltaAmountMinor",
+ "PricingModifierRange",
"PriorityResource",
"ProfileColumnExpr",
"ProfileColumnExprProfile",
@@ -2448,6 +2573,7 @@ def __dir__():
"RawSleepResponse",
"RawWorkout",
"RawWorkoutResponse",
+ "ReflexPricingCondition",
"Region",
"RelatedCandidate",
"RelativeTimeframe",
@@ -2456,6 +2582,7 @@ def __dir__():
"ResponsibleRelationship",
"ResultMetadata",
"ResultMetadataGender",
+ "ResultStatus",
"ResultType",
"SampleData",
"SampleDataDateCollected",
@@ -2468,6 +2595,7 @@ def __dir__():
"ScopeRequirementsStr",
"SearchCompendiumResponse",
"SearchMode",
+ "SearchOrderableTestsResponse",
"Select",
"Sex",
"SexualActivityEntry",
@@ -2492,6 +2620,7 @@ def __dir__():
"SourceColumnExprSource",
"SourceLink",
"SourceType",
+ "SpecifiedPricingMarkerPricingConditions",
"TeamConfig",
"TemperatureTimeseriesExpr",
"TemperatureTimeseriesExprField",
@@ -2500,12 +2629,24 @@ def __dir__():
"TimeseriesMetricPoint",
"TimeseriesResource",
"TraceElements",
+ "UnmatchedResult",
+ "UnmatchedResultResolutionAction",
+ "UnmatchedResultTestCase",
+ "UnmatchedResultTestCaseDefinition",
+ "UnmatchedResultTestFailure",
+ "UnmatchedResultTestOrderArtifact",
+ "UnmatchedResultTestOrderSource",
+ "UnmatchedResultTestRunStatus",
+ "UnmatchedResultTestStage",
"UnnestExpr",
"UnnestExprUnnest",
"UnprocessableEntityError",
"UnrecognizedValueMacroExpr",
+ "UnspecifiedPricing",
+ "UnspecifiedPricingUnspecified",
"UsAddress",
"UsState",
+ "UsStatePricingCondition",
"UserAddress",
"UserHistoricalPullsResponse",
"UserInfo",
diff --git a/src/junction/client.py b/src/junction/client.py
index e14ec29..f508733 100644
--- a/src/junction/client.py
+++ b/src/junction/client.py
@@ -66,6 +66,12 @@ class Junction:
max_retries : typing.Optional[int]
The default maximum number of retries for failed requests. Defaults to 2. Per-request `max_retries` in `request_options` takes precedence over this value.
+ stream_reconnection_enabled : typing.Optional[bool]
+ Whether to automatically reconnect on stream disconnection for resumable streaming endpoints. Defaults to True. Per-request `stream_reconnection_enabled` in `request_options` takes precedence over this value.
+
+ max_stream_reconnection_attempts : typing.Optional[int]
+ The maximum number of reconnection attempts for resumable streaming endpoints. Defaults to no limit. Per-request `max_stream_reconnection_attempts` in `request_options` takes precedence over this value.
+
follow_redirects : typing.Optional[bool]
Whether the default httpx client follows redirects or not, this is irrelevant if a custom httpx client is passed in.
@@ -93,13 +99,13 @@ def __init__(
headers: typing.Optional[typing.Dict[str, str]] = None,
timeout: typing.Optional[float] = None,
max_retries: typing.Optional[int] = None,
+ stream_reconnection_enabled: typing.Optional[bool] = None,
+ max_stream_reconnection_attempts: typing.Optional[int] = None,
follow_redirects: typing.Optional[bool] = True,
httpx_client: typing.Optional[httpx.Client] = None,
logging: typing.Optional[typing.Union[LogConfig, Logger]] = None,
):
- _defaulted_timeout = (
- timeout if timeout is not None else 60 if httpx_client is None else httpx_client.timeout.read
- )
+ _defaulted_timeout = timeout if timeout is not None else 60 if httpx_client is None else None
_defaulted_max_retries = max_retries if max_retries is not None else 2
self._client_wrapper = SyncClientWrapper(
base_url=_get_base_url(base_url=base_url, environment=environment),
@@ -112,6 +118,8 @@ def __init__(
else httpx.Client(timeout=_defaulted_timeout),
timeout=_defaulted_timeout,
max_retries=_defaulted_max_retries,
+ stream_reconnection_enabled=stream_reconnection_enabled,
+ max_stream_reconnection_attempts=max_stream_reconnection_attempts,
logging=logging,
)
self._link: typing.Optional[LinkClient] = None
@@ -396,6 +404,12 @@ class AsyncJunction:
max_retries : typing.Optional[int]
The default maximum number of retries for failed requests. Defaults to 2. Per-request `max_retries` in `request_options` takes precedence over this value.
+ stream_reconnection_enabled : typing.Optional[bool]
+ Whether to automatically reconnect on stream disconnection for resumable streaming endpoints. Defaults to True. Per-request `stream_reconnection_enabled` in `request_options` takes precedence over this value.
+
+ max_stream_reconnection_attempts : typing.Optional[int]
+ The maximum number of reconnection attempts for resumable streaming endpoints. Defaults to no limit. Per-request `max_stream_reconnection_attempts` in `request_options` takes precedence over this value.
+
follow_redirects : typing.Optional[bool]
Whether the default httpx client follows redirects or not, this is irrelevant if a custom httpx client is passed in.
@@ -423,13 +437,13 @@ def __init__(
headers: typing.Optional[typing.Dict[str, str]] = None,
timeout: typing.Optional[float] = None,
max_retries: typing.Optional[int] = None,
+ stream_reconnection_enabled: typing.Optional[bool] = None,
+ max_stream_reconnection_attempts: typing.Optional[int] = None,
follow_redirects: typing.Optional[bool] = True,
httpx_client: typing.Optional[httpx.AsyncClient] = None,
logging: typing.Optional[typing.Union[LogConfig, Logger]] = None,
):
- _defaulted_timeout = (
- timeout if timeout is not None else 60 if httpx_client is None else httpx_client.timeout.read
- )
+ _defaulted_timeout = timeout if timeout is not None else 60 if httpx_client is None else None
_defaulted_max_retries = max_retries if max_retries is not None else 2
self._client_wrapper = AsyncClientWrapper(
base_url=_get_base_url(base_url=base_url, environment=environment),
@@ -440,6 +454,8 @@ def __init__(
else _make_default_async_client(timeout=_defaulted_timeout, follow_redirects=follow_redirects),
timeout=_defaulted_timeout,
max_retries=_defaulted_max_retries,
+ stream_reconnection_enabled=stream_reconnection_enabled,
+ max_stream_reconnection_attempts=max_stream_reconnection_attempts,
logging=logging,
)
self._link: typing.Optional[AsyncLinkClient] = None
diff --git a/src/junction/compendium/client.py b/src/junction/compendium/client.py
index 9365de4..1507935 100644
--- a/src/junction/compendium/client.py
+++ b/src/junction/compendium/client.py
@@ -8,6 +8,7 @@
from ..types.convert_compendium_response import ConvertCompendiumResponse
from ..types.search_compendium_response import SearchCompendiumResponse
from ..types.search_mode import SearchMode
+from ..types.search_orderable_tests_response import SearchOrderableTestsResponse
from .raw_client import AsyncRawCompendiumClient, RawCompendiumClient
# this is used as the default value for optional parameters
@@ -139,6 +140,52 @@ def convert(
)
return _response.data
+ def search_orderable_tests(
+ self,
+ *,
+ provider_ids: typing.Sequence[str],
+ target_lab: CompendiumSearchLabs,
+ per_provider_limit: typing.Optional[int] = OMIT,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> SearchOrderableTestsResponse:
+ """
+ Parameters
+ ----------
+ provider_ids : typing.Sequence[str]
+
+ target_lab : CompendiumSearchLabs
+ ℹ️ This enum is non-exhaustive.
+
+ per_provider_limit : typing.Optional[int]
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ SearchOrderableTestsResponse
+ Successful Response
+
+ Examples
+ --------
+ from junction import CompendiumSearchLabs, Junction
+
+ client = Junction(
+ api_key="YOUR_API_KEY",
+ )
+ client.compendium.search_orderable_tests(
+ provider_ids=["provider_ids"],
+ target_lab=CompendiumSearchLabs.LABCORP,
+ )
+ """
+ _response = self._raw_client.search_orderable_tests(
+ provider_ids=provider_ids,
+ target_lab=target_lab,
+ per_provider_limit=per_provider_limit,
+ request_options=request_options,
+ )
+ return _response.data
+
class AsyncCompendiumClient:
def __init__(self, *, client_wrapper: AsyncClientWrapper):
@@ -280,3 +327,57 @@ async def main() -> None:
request_options=request_options,
)
return _response.data
+
+ async def search_orderable_tests(
+ self,
+ *,
+ provider_ids: typing.Sequence[str],
+ target_lab: CompendiumSearchLabs,
+ per_provider_limit: typing.Optional[int] = OMIT,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> SearchOrderableTestsResponse:
+ """
+ Parameters
+ ----------
+ provider_ids : typing.Sequence[str]
+
+ target_lab : CompendiumSearchLabs
+ ℹ️ This enum is non-exhaustive.
+
+ per_provider_limit : typing.Optional[int]
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ SearchOrderableTestsResponse
+ Successful Response
+
+ Examples
+ --------
+ import asyncio
+
+ from junction import AsyncJunction, CompendiumSearchLabs
+
+ client = AsyncJunction(
+ api_key="YOUR_API_KEY",
+ )
+
+
+ async def main() -> None:
+ await client.compendium.search_orderable_tests(
+ provider_ids=["provider_ids"],
+ target_lab=CompendiumSearchLabs.LABCORP,
+ )
+
+
+ asyncio.run(main())
+ """
+ _response = await self._raw_client.search_orderable_tests(
+ provider_ids=provider_ids,
+ target_lab=target_lab,
+ per_provider_limit=per_provider_limit,
+ request_options=request_options,
+ )
+ return _response.data
diff --git a/src/junction/compendium/raw_client.py b/src/junction/compendium/raw_client.py
index 1122de3..69eb204 100644
--- a/src/junction/compendium/raw_client.py
+++ b/src/junction/compendium/raw_client.py
@@ -15,6 +15,7 @@
from ..types.http_validation_error import HttpValidationError
from ..types.search_compendium_response import SearchCompendiumResponse
from ..types.search_mode import SearchMode
+from ..types.search_orderable_tests_response import SearchOrderableTestsResponse
from pydantic import ValidationError
# this is used as the default value for optional parameters
@@ -185,6 +186,76 @@ def convert(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+ def search_orderable_tests(
+ self,
+ *,
+ provider_ids: typing.Sequence[str],
+ target_lab: CompendiumSearchLabs,
+ per_provider_limit: typing.Optional[int] = OMIT,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> HttpResponse[SearchOrderableTestsResponse]:
+ """
+ Parameters
+ ----------
+ provider_ids : typing.Sequence[str]
+
+ target_lab : CompendiumSearchLabs
+ ℹ️ This enum is non-exhaustive.
+
+ per_provider_limit : typing.Optional[int]
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ HttpResponse[SearchOrderableTestsResponse]
+ Successful Response
+ """
+ _response = self._client_wrapper.httpx_client.request(
+ "v3/compendium/search_orderable_tests",
+ method="POST",
+ json={
+ "provider_ids": provider_ids,
+ "target_lab": target_lab,
+ "per_provider_limit": per_provider_limit,
+ },
+ headers={
+ "content-type": "application/json",
+ },
+ request_options=request_options,
+ omit=OMIT,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ SearchOrderableTestsResponse,
+ parse_obj_as(
+ type_=SearchOrderableTestsResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return HttpResponse(response=_response, data=_data)
+ if _response.status_code == 422:
+ raise UnprocessableEntityError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ HttpValidationError,
+ parse_obj_as(
+ type_=HttpValidationError, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
class AsyncRawCompendiumClient:
def __init__(self, *, client_wrapper: AsyncClientWrapper):
@@ -349,3 +420,73 @@ async def convert(
status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ async def search_orderable_tests(
+ self,
+ *,
+ provider_ids: typing.Sequence[str],
+ target_lab: CompendiumSearchLabs,
+ per_provider_limit: typing.Optional[int] = OMIT,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> AsyncHttpResponse[SearchOrderableTestsResponse]:
+ """
+ Parameters
+ ----------
+ provider_ids : typing.Sequence[str]
+
+ target_lab : CompendiumSearchLabs
+ ℹ️ This enum is non-exhaustive.
+
+ per_provider_limit : typing.Optional[int]
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[SearchOrderableTestsResponse]
+ Successful Response
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ "v3/compendium/search_orderable_tests",
+ method="POST",
+ json={
+ "provider_ids": provider_ids,
+ "target_lab": target_lab,
+ "per_provider_limit": per_provider_limit,
+ },
+ headers={
+ "content-type": "application/json",
+ },
+ request_options=request_options,
+ omit=OMIT,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ SearchOrderableTestsResponse,
+ parse_obj_as(
+ type_=SearchOrderableTestsResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 422:
+ raise UnprocessableEntityError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ HttpValidationError,
+ parse_obj_as(
+ type_=HttpValidationError, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
diff --git a/src/junction/core/client_wrapper.py b/src/junction/core/client_wrapper.py
index 9d9fa91..eb1c055 100644
--- a/src/junction/core/client_wrapper.py
+++ b/src/junction/core/client_wrapper.py
@@ -16,6 +16,8 @@ def __init__(
base_url: str,
timeout: typing.Optional[float] = None,
max_retries: int = 2,
+ stream_reconnection_enabled: typing.Optional[bool] = None,
+ max_stream_reconnection_attempts: typing.Optional[int] = None,
logging: typing.Optional[typing.Union[LogConfig, Logger]] = None,
):
self.api_key = api_key
@@ -23,18 +25,20 @@ def __init__(
self._base_url = base_url
self._timeout = timeout
self._max_retries = max_retries
+ self._stream_reconnection_enabled = stream_reconnection_enabled
+ self._max_stream_reconnection_attempts = max_stream_reconnection_attempts
self._logging = logging
def get_headers(self) -> typing.Dict[str, str]:
import platform
headers: typing.Dict[str, str] = {
- "User-Agent": "junction-api-sdk/1.3.0",
+ "User-Agent": "junction-api-sdk/0.0.0-fern-placeholder",
"X-Fern-Language": "Python",
"X-Fern-Runtime": f"python/{platform.python_version()}",
"X-Fern-Platform": f"{platform.system().lower()}/{platform.release()}",
"X-Fern-SDK-Name": "junction-api-sdk",
- "X-Fern-SDK-Version": "1.3.0",
+ "X-Fern-SDK-Version": "1.4.0",
**(self.get_custom_headers() or {}),
}
headers["x-vital-api-key"] = self.api_key
@@ -52,6 +56,12 @@ def get_timeout(self) -> typing.Optional[float]:
def get_max_retries(self) -> int:
return self._max_retries
+ def get_stream_reconnection_enabled(self) -> bool:
+ return self._stream_reconnection_enabled if self._stream_reconnection_enabled is not None else True
+
+ def get_max_stream_reconnection_attempts(self) -> typing.Optional[int]:
+ return self._max_stream_reconnection_attempts
+
class SyncClientWrapper(BaseClientWrapper):
def __init__(
@@ -62,6 +72,8 @@ def __init__(
base_url: str,
timeout: typing.Optional[float] = None,
max_retries: int = 2,
+ stream_reconnection_enabled: typing.Optional[bool] = None,
+ max_stream_reconnection_attempts: typing.Optional[int] = None,
logging: typing.Optional[typing.Union[LogConfig, Logger]] = None,
httpx_client: httpx.Client,
):
@@ -71,6 +83,8 @@ def __init__(
base_url=base_url,
timeout=timeout,
max_retries=max_retries,
+ stream_reconnection_enabled=stream_reconnection_enabled,
+ max_stream_reconnection_attempts=max_stream_reconnection_attempts,
logging=logging,
)
self.httpx_client = HttpClient(
@@ -92,6 +106,8 @@ def __init__(
base_url: str,
timeout: typing.Optional[float] = None,
max_retries: int = 2,
+ stream_reconnection_enabled: typing.Optional[bool] = None,
+ max_stream_reconnection_attempts: typing.Optional[int] = None,
logging: typing.Optional[typing.Union[LogConfig, Logger]] = None,
async_token: typing.Optional[typing.Callable[[], typing.Awaitable[str]]] = None,
httpx_client: httpx.AsyncClient,
@@ -102,6 +118,8 @@ def __init__(
base_url=base_url,
timeout=timeout,
max_retries=max_retries,
+ stream_reconnection_enabled=stream_reconnection_enabled,
+ max_stream_reconnection_attempts=max_stream_reconnection_attempts,
logging=logging,
)
self._async_token = async_token
diff --git a/src/junction/core/http_client.py b/src/junction/core/http_client.py
index f686c57..409d267 100644
--- a/src/junction/core/http_client.py
+++ b/src/junction/core/http_client.py
@@ -3,6 +3,7 @@
import asyncio
import email.utils
import re
+import socket
import time
import typing
from contextlib import asynccontextmanager, contextmanager
@@ -23,6 +24,39 @@
JITTER_FACTOR = 0.2 # 20% random jitter
+def get_keepalive_socket_options(
+ idle: int = 60,
+ intvl: int = 30,
+ cnt: int = 5,
+) -> typing.List[typing.Tuple[int, int, int]]:
+ """
+ Build TCP keepalive socket options for the current platform.
+
+ Keepalive probes keep otherwise-idle connections alive so that long,
+ non-streaming requests survive idle-connection reaping by a firewall,
+ load balancer, or NAT. The available socket constants are OS-dependent,
+ so each option is guarded and only emitted when the platform defines it:
+
+ - ``SO_KEEPALIVE`` is portable (Linux/macOS/Windows).
+ - The idle-before-first-probe knob is ``TCP_KEEPIDLE`` on Linux and modern
+ Windows, but ``TCP_KEEPALIVE`` on macOS.
+ - ``TCP_KEEPINTVL`` / ``TCP_KEEPCNT`` exist on Linux/macOS/modern Windows.
+
+ Passing these tuples to ``httpx.HTTPTransport(socket_options=...)`` /
+ ``httpx.AsyncHTTPTransport(socket_options=...)`` applies them to every
+ connection the transport opens.
+ """
+ opts: typing.List[typing.Tuple[int, int, int]] = [(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)]
+ idle_const = getattr(socket, "TCP_KEEPIDLE", None) or getattr(socket, "TCP_KEEPALIVE", None)
+ if idle_const:
+ opts.append((socket.IPPROTO_TCP, idle_const, idle))
+ if hasattr(socket, "TCP_KEEPINTVL"):
+ opts.append((socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, intvl))
+ if hasattr(socket, "TCP_KEEPCNT"):
+ opts.append((socket.IPPROTO_TCP, socket.TCP_KEEPCNT, cnt))
+ return opts
+
+
def _parse_retry_after(response_headers: httpx.Headers) -> typing.Optional[float]:
"""
This function parses the `Retry-After` header in a HTTP response and returns the number of seconds to wait.
@@ -312,11 +346,14 @@ def request(
force_multipart: typing.Optional[bool] = None,
) -> httpx.Response:
base_url = self.get_base_url(base_url)
- timeout = (
- request_options.get("timeout_in_seconds")
+ _timeout = (
+ request_options.get("timeout")
+ if request_options is not None and request_options.get("timeout") is not None
+ else request_options.get("timeout_in_seconds")
if request_options is not None and request_options.get("timeout_in_seconds") is not None
else self.base_timeout()
)
+ timeout = _timeout if _timeout is not None else httpx.USE_CLIENT_DEFAULT
json_body, data_body = get_request_body(json=json, data=data, request_options=request_options, omit=omit)
@@ -472,11 +509,14 @@ def stream(
force_multipart: typing.Optional[bool] = None,
) -> typing.Iterator[httpx.Response]:
base_url = self.get_base_url(base_url)
- timeout = (
- request_options.get("timeout_in_seconds")
+ _timeout = (
+ request_options.get("timeout")
+ if request_options is not None and request_options.get("timeout") is not None
+ else request_options.get("timeout_in_seconds")
if request_options is not None and request_options.get("timeout_in_seconds") is not None
else self.base_timeout()
)
+ timeout = _timeout if _timeout is not None else httpx.USE_CLIENT_DEFAULT
request_files: typing.Optional[RequestFiles] = (
convert_file_dict_to_httpx_tuples(remove_omit_from_dict(remove_none_from_dict(files), omit))
@@ -601,11 +641,14 @@ async def request(
force_multipart: typing.Optional[bool] = None,
) -> httpx.Response:
base_url = self.get_base_url(base_url)
- timeout = (
- request_options.get("timeout_in_seconds")
+ _timeout = (
+ request_options.get("timeout")
+ if request_options is not None and request_options.get("timeout") is not None
+ else request_options.get("timeout_in_seconds")
if request_options is not None and request_options.get("timeout_in_seconds") is not None
else self.base_timeout()
)
+ timeout = _timeout if _timeout is not None else httpx.USE_CLIENT_DEFAULT
request_files: typing.Optional[RequestFiles] = (
convert_file_dict_to_httpx_tuples(remove_omit_from_dict(remove_none_from_dict(files), omit))
@@ -764,11 +807,14 @@ async def stream(
force_multipart: typing.Optional[bool] = None,
) -> typing.AsyncIterator[httpx.Response]:
base_url = self.get_base_url(base_url)
- timeout = (
- request_options.get("timeout_in_seconds")
+ _timeout = (
+ request_options.get("timeout")
+ if request_options is not None and request_options.get("timeout") is not None
+ else request_options.get("timeout_in_seconds")
if request_options is not None and request_options.get("timeout_in_seconds") is not None
else self.base_timeout()
)
+ timeout = _timeout if _timeout is not None else httpx.USE_CLIENT_DEFAULT
request_files: typing.Optional[RequestFiles] = (
convert_file_dict_to_httpx_tuples(remove_omit_from_dict(remove_none_from_dict(files), omit))
diff --git a/src/junction/core/http_sse/_api.py b/src/junction/core/http_sse/_api.py
index b375399..9ca5602 100644
--- a/src/junction/core/http_sse/_api.py
+++ b/src/junction/core/http_sse/_api.py
@@ -2,29 +2,81 @@
import codecs
import re
+import time
from contextlib import asynccontextmanager, contextmanager
-from typing import Any, AsyncGenerator, AsyncIterator, Iterator
+from typing import (
+ Any,
+ AsyncContextManager,
+ AsyncGenerator,
+ AsyncIterator,
+ Callable,
+ ContextManager,
+ Iterator,
+ Optional,
+)
+import anyio
import httpx
from ._decoders import SSEDecoder
from ._exceptions import SSEError
from ._models import ServerSentEvent
+MAX_LINE_SIZE: int = 1_048_576 # 1 MiB
+# Reconnection defaults, mirroring the TypeScript SDK's Stream implementation.
+DEFAULT_MAX_RECONNECTION_ATTEMPTS: int = 5
+DEFAULT_RECONNECT_DELAY_MS: int = 1_000
+MAX_RECONNECT_DELAY_MS: int = 30_000
+
+
+# A reconnect callback re-issues the original request (with a ``Last-Event-ID``
+# header set to the supplied event id) and returns a *context manager* yielding
+# a fresh streaming ``httpx.Response``. Sync clients supply a sync context
+# manager; async clients supply an async one.
class EventSource:
- def __init__(self, response: httpx.Response) -> None:
+ def __init__(
+ self,
+ response: httpx.Response,
+ *,
+ resumable: bool = False,
+ stream_reconnection_enabled: bool = True,
+ max_stream_reconnection_attempts: Optional[int] = None,
+ stream_terminator: Optional[str] = None,
+ reconnect: Optional[Callable[[str], Any]] = None,
+ ) -> None:
self._response = response
+ self._resumable = resumable
+ self._stream_reconnection_enabled = stream_reconnection_enabled
+ self._max_stream_reconnection_attempts = max_stream_reconnection_attempts
+ self._stream_terminator = stream_terminator
+ self._reconnect = reconnect
+
+ @staticmethod
+ def _is_event_stream(response: httpx.Response) -> bool:
+ content_type = response.headers.get("content-type", "").partition(";")[0]
+ return "text/event-stream" in content_type
def _check_content_type(self) -> None:
- content_type = self._response.headers.get("content-type", "").partition(";")[0]
- if "text/event-stream" not in content_type:
+ if not self._is_event_stream(self._response):
+ content_type = self._response.headers.get("content-type", "").partition(";")[0]
raise SSEError(
f"Expected response header Content-Type to contain 'text/event-stream', got {content_type!r}"
)
- def _get_charset(self) -> str:
+ def _is_reconnect_response_usable(self, response: httpx.Response) -> bool:
+ """Whether a reconnected response can be resumed as an SSE stream.
+
+ ``httpx.stream`` does not raise on non-success status, so a resume that
+ returns an error page (e.g. ``200 text/html`` or a ``500`` body) would
+ otherwise be parsed as SSE and yield garbage/zero events. Such a
+ response is treated as a failed attempt (back off and retry) instead.
+ """
+ return response.status_code < 400 and self._is_event_stream(response)
+
+ def _get_charset(self, response: Optional[httpx.Response] = None) -> str:
"""Extract charset from Content-Type header, fallback to UTF-8."""
- content_type = self._response.headers.get("content-type", "")
+ resolved = response if response is not None else self._response
+ content_type = resolved.headers.get("content-type", "")
# Parse charset parameter using regex
charset_match = re.search(r"charset=([^;\s]+)", content_type, re.IGNORECASE)
@@ -58,14 +110,83 @@ def _normalize_sse_line_endings(buf: str) -> str:
return buf[:-1].replace("\r", "\n") + "\r"
return buf.replace("\r", "\n")
- def iter_sse(self) -> Iterator[ServerSentEvent]:
- self._check_content_type()
- decoder = SSEDecoder()
- charset = self._get_charset()
- text_decoder = codecs.getincrementaldecoder(charset)(errors="replace")
+ def _new_text_decoder(self, response: Optional[httpx.Response] = None) -> "codecs.IncrementalDecoder":
+ return codecs.getincrementaldecoder(self._get_charset(response))(errors="replace")
+
+ def _reconnect_applicable(self) -> bool:
+ """Whether reconnection is configured for this stream at all.
+
+ This is the terminator-gating half of the reconnect decision, kept
+ separate from :meth:`_should_reconnect` (which additionally requires a
+ last *dispatched* id and an unexhausted attempt budget). The split lets
+ a mid-stream transport error terminate consistently:
+ - a stream that can never reconnect (non-resumable, no terminator,
+ disabled, or no callback) must re-raise the error to the caller, so a
+ truncated stream is not mistaken for a clean completion;
+ - a resumable stream that has merely run out of attempts (or has no id
+ to resume from) ends cleanly — the same way an exhausted empty/error
+ -body resume already does, matching the TypeScript ``return``.
+ """
+ return (
+ self._resumable
+ and self._stream_terminator is not None
+ and self._stream_reconnection_enabled
+ and self._reconnect is not None
+ )
+
+ def _should_reconnect(self, last_dispatched_id: Optional[str], reconnect_attempts: int) -> bool:
+ """Decide whether a prematurely-ended stream should be reconnected.
+
+ Mirrors the TypeScript ``shouldReconnect`` gating:
+ - only resumable SSE endpoints with a configured terminator, reconnect
+ enabled, and a reconnect callback are eligible (see
+ :meth:`_reconnect_applicable`);
+ - a last *dispatched* event id must exist to resume from;
+ - the consecutive-failed-attempt cap must not be exceeded.
+ """
+ if not self._reconnect_applicable():
+ return False
+ if not last_dispatched_id:
+ return False
+ max_attempts = (
+ self._max_stream_reconnection_attempts
+ if self._max_stream_reconnection_attempts is not None
+ else DEFAULT_MAX_RECONNECTION_ATTEMPTS
+ )
+ if reconnect_attempts >= max_attempts:
+ return False
+ return True
+
+ def _reconnect_delay_seconds(self, last_retry: Optional[int]) -> float:
+ """Backoff before a reconnect.
+ Uses the server's most recent ``retry:`` directive (milliseconds) when
+ present, otherwise a default of ``DEFAULT_RECONNECT_DELAY_MS``, clamped
+ to ``MAX_RECONNECT_DELAY_MS``.
+ """
+ base_ms = last_retry if (last_retry is not None and last_retry > 0) else DEFAULT_RECONNECT_DELAY_MS
+ return min(base_ms, MAX_RECONNECT_DELAY_MS) / 1000.0
+
+ def _sleep_before_reconnect(self, last_retry: Optional[int]) -> None:
+ # ``time.sleep`` blocks the calling thread but remains interruptible by
+ # signals (e.g. ``KeyboardInterrupt``), which propagate out and abort
+ # the reconnect without issuing another request.
+ time.sleep(self._reconnect_delay_seconds(last_retry))
+
+ async def _asleep_before_reconnect(self, last_retry: Optional[int]) -> None:
+ # ``anyio.sleep`` is cancellation-aware: if the consumer cancels the task
+ # or closes the async generator mid-delay, this raises (and no further
+ # request is issued) instead of blocking for the whole interval.
+ await anyio.sleep(self._reconnect_delay_seconds(last_retry))
+
+ def _decode_response(
+ self,
+ response: httpx.Response,
+ decoder: SSEDecoder,
+ text_decoder: "codecs.IncrementalDecoder",
+ ) -> Iterator[ServerSentEvent]:
buf = ""
- for chunk in self._response.iter_bytes():
+ for chunk in response.iter_bytes():
buf += text_decoder.decode(chunk)
buf = self._normalize_sse_line_endings(buf)
@@ -75,29 +196,21 @@ def iter_sse(self) -> Iterator[ServerSentEvent]:
if sse is not None:
yield sse
- # Flush any remaining bytes from the incremental decoder
- buf += text_decoder.decode(b"", final=True)
- buf = buf.replace("\r\n", "\n").replace("\r", "\n")
+ if len(buf) > MAX_LINE_SIZE:
+ raise SSEError(
+ f"SSE line exceeded maximum size of {MAX_LINE_SIZE} characters without encountering a newline"
+ )
- while "\n" in buf:
- line, buf = buf.split("\n", 1)
- sse = decoder.decode(line)
- if sse is not None:
- yield sse
-
- if buf.strip():
- sse = decoder.decode(buf)
- if sse is not None:
- yield sse
-
- async def aiter_sse(self) -> AsyncGenerator[ServerSentEvent, None]:
- self._check_content_type()
- decoder = SSEDecoder()
- charset = self._get_charset()
- text_decoder = codecs.getincrementaldecoder(charset)(errors="replace")
+ yield from self._flush_decoder(buf, decoder, text_decoder)
+ async def _adecode_response(
+ self,
+ response: httpx.Response,
+ decoder: SSEDecoder,
+ text_decoder: "codecs.IncrementalDecoder",
+ ) -> AsyncGenerator[ServerSentEvent, None]:
buf = ""
- async for chunk in self._response.aiter_bytes():
+ async for chunk in response.aiter_bytes():
buf += text_decoder.decode(chunk)
buf = self._normalize_sse_line_endings(buf)
@@ -107,10 +220,29 @@ async def aiter_sse(self) -> AsyncGenerator[ServerSentEvent, None]:
if sse is not None:
yield sse
+ if len(buf) > MAX_LINE_SIZE:
+ raise SSEError(
+ f"SSE line exceeded maximum size of {MAX_LINE_SIZE} characters without encountering a newline"
+ )
+
+ for sse in self._flush_decoder(buf, decoder, text_decoder):
+ yield sse
+
+ def _flush_decoder(
+ self,
+ buf: str,
+ decoder: SSEDecoder,
+ text_decoder: "codecs.IncrementalDecoder",
+ ) -> Iterator[ServerSentEvent]:
# Flush any remaining bytes from the incremental decoder
buf += text_decoder.decode(b"", final=True)
buf = buf.replace("\r\n", "\n").replace("\r", "\n")
+ if len(buf) > MAX_LINE_SIZE:
+ raise SSEError(
+ f"SSE line exceeded maximum size of {MAX_LINE_SIZE} characters without encountering a newline"
+ )
+
while "\n" in buf:
line, buf = buf.split("\n", 1)
sse = decoder.decode(line)
@@ -122,6 +254,181 @@ async def aiter_sse(self) -> AsyncGenerator[ServerSentEvent, None]:
if sse is not None:
yield sse
+ def iter_sse(self) -> Iterator[ServerSentEvent]:
+ self._check_content_type()
+ decoder = SSEDecoder()
+ text_decoder = self._new_text_decoder()
+
+ last_dispatched_id: Optional[str] = None
+ last_retry: Optional[int] = None
+ # Consecutive failed reconnection attempts. Reset to 0 whenever an event
+ # is successfully dispatched (reset-on-progress) — matching browser
+ # `EventSource` semantics: a server that emits >=1 event then drops on
+ # every connection can reconnect indefinitely.
+ reconnect_attempts = 0
+
+ # ``None`` means there is no live stream to read this iteration (e.g. a
+ # failed reconnect); the loop then re-evaluates the reconnect decision
+ # without re-reading an exhausted response.
+ response: Optional[httpx.Response] = self._response
+ # Context manager for a response we opened ourselves and must close.
+ # The initial response is owned by the caller, so it starts as None.
+ owned_cm: Optional[ContextManager[httpx.Response]] = None
+ try:
+ while True:
+ if response is not None:
+ events = self._decode_response(response, decoder, text_decoder)
+ while True:
+ try:
+ sse = next(events)
+ except StopIteration:
+ break
+ except SSEError:
+ # A protocol violation (e.g. an oversized line) is a
+ # genuine error, not a dropped connection; propagate it.
+ # Listed first because ``SSEError`` subclasses
+ # ``httpx.TransportError``.
+ raise
+ except httpx.TransportError:
+ # A transport error mid-stream (e.g. the server dropped
+ # the connection: ``ReadError``/``RemoteProtocolError``)
+ # is a premature end. Only swallow it when reconnection
+ # is configured for this stream; otherwise re-raise so a
+ # non-resumable stream still surfaces the error to the
+ # caller instead of looking like a clean completion.
+ # When reconnection is applicable but the attempt budget
+ # is exhausted, we ``break`` and end cleanly below — the
+ # same way an exhausted empty/error-body resume does, so
+ # give-up is consistent regardless of failure shape.
+ # ``next`` is used rather than ``for`` so this cannot
+ # swallow a ``GeneratorExit`` raised at a ``yield``.
+ if not self._reconnect_applicable():
+ raise
+ break
+ yield sse
+ if sse.id:
+ last_dispatched_id = sse.id
+ if sse.retry is not None:
+ last_retry = sse.retry
+ reconnect_attempts = 0
+
+ if not self._should_reconnect(last_dispatched_id, reconnect_attempts):
+ return
+ reconnect_attempts += 1
+
+ self._sleep_before_reconnect(last_retry)
+
+ # Close the previously-opened reconnect response before opening
+ # a new one so we never hold more than one extra connection.
+ if owned_cm is not None:
+ owned_cm.__exit__(None, None, None)
+ owned_cm = None
+
+ assert self._reconnect is not None # guaranteed by _should_reconnect
+ try:
+ cm: ContextManager[httpx.Response] = self._reconnect(last_dispatched_id or "")
+ new_response = cm.__enter__()
+ except Exception:
+ # A failed reconnect consumes an attempt; back off and retry.
+ response = None
+ continue
+ owned_cm = cm
+ if new_response is None or not self._is_reconnect_response_usable(new_response):
+ # Null/empty body or a non-SSE/error response (e.g. 204/304,
+ # a 500, or an HTML error page): treat as a failed attempt.
+ response = None
+ continue
+
+ response = new_response
+ # Drop any partial event left over from the dropped stream, but
+ # keep the last event id (per the SSE spec) and start a fresh
+ # incremental text decoder for the new connection.
+ decoder.reset_in_progress_event()
+ text_decoder = self._new_text_decoder(new_response)
+ finally:
+ if owned_cm is not None:
+ owned_cm.__exit__(None, None, None)
+
+ async def aiter_sse(self) -> AsyncGenerator[ServerSentEvent, None]:
+ self._check_content_type()
+ decoder = SSEDecoder()
+ text_decoder = self._new_text_decoder()
+
+ last_dispatched_id: Optional[str] = None
+ last_retry: Optional[int] = None
+ reconnect_attempts = 0
+
+ response: Optional[httpx.Response] = self._response
+ owned_cm: Optional[AsyncContextManager[httpx.Response]] = None
+ try:
+ while True:
+ if response is not None:
+ events = self._adecode_response(response, decoder, text_decoder)
+ while True:
+ try:
+ sse = await events.__anext__()
+ except StopAsyncIteration:
+ break
+ except SSEError:
+ # A protocol violation (e.g. an oversized line) is a
+ # genuine error, not a dropped connection; propagate it.
+ # Listed first because ``SSEError`` subclasses
+ # ``httpx.TransportError``.
+ raise
+ except httpx.TransportError:
+ # A transport error mid-stream (e.g. the server dropped
+ # the connection: ``ReadError``/``RemoteProtocolError``)
+ # is a premature end. Only swallow it when reconnection
+ # is configured for this stream; otherwise re-raise so a
+ # non-resumable stream still surfaces the error to the
+ # caller instead of looking like a clean completion.
+ # When reconnection is applicable but the attempt budget
+ # is exhausted, we ``break`` and end cleanly below — the
+ # same way an exhausted empty/error-body resume does, so
+ # give-up is consistent regardless of failure shape.
+ if not self._reconnect_applicable():
+ raise
+ break
+ yield sse
+ if sse.id:
+ last_dispatched_id = sse.id
+ if sse.retry is not None:
+ last_retry = sse.retry
+ reconnect_attempts = 0
+
+ if not self._should_reconnect(last_dispatched_id, reconnect_attempts):
+ return
+ reconnect_attempts += 1
+
+ await self._asleep_before_reconnect(last_retry)
+
+ if owned_cm is not None:
+ await owned_cm.__aexit__(None, None, None)
+ owned_cm = None
+
+ assert self._reconnect is not None # guaranteed by _should_reconnect
+ try:
+ cm: AsyncContextManager[httpx.Response] = self._reconnect(last_dispatched_id or "")
+ new_response = await cm.__aenter__()
+ except Exception:
+ response = None
+ continue
+ owned_cm = cm
+ if new_response is None or not self._is_reconnect_response_usable(new_response):
+ response = None
+ continue
+
+ response = new_response
+ decoder.reset_in_progress_event()
+ text_decoder = self._new_text_decoder(new_response)
+ finally:
+ if owned_cm is not None:
+ # Shield the close so a cancellation delivered while reading a
+ # reconnected response still fully tears the connection down
+ # instead of leaking it until the client is closed.
+ with anyio.CancelScope(shield=True):
+ await owned_cm.__aexit__(None, None, None)
+
@contextmanager
def connect_sse(client: httpx.Client, method: str, url: str, **kwargs: Any) -> Iterator[EventSource]:
diff --git a/src/junction/core/http_sse/_decoders.py b/src/junction/core/http_sse/_decoders.py
index 339b089..1f6b35e 100644
--- a/src/junction/core/http_sse/_decoders.py
+++ b/src/junction/core/http_sse/_decoders.py
@@ -12,6 +12,19 @@ def __init__(self) -> None:
self._last_event_id = ""
self._retry: Optional[int] = None
+ def reset_in_progress_event(self) -> None:
+ """Discard any partially-parsed (undispatched) event.
+
+ Used when a stream ends mid-event before reconnecting: the buffered
+ ``event``/``data``/``retry`` fields of the never-dispatched event must
+ be dropped so they do not corrupt the first event of the reconnected
+ stream. Per the SSE spec the last event id is *not* reset here — it
+ persists across connections.
+ """
+ self._event = ""
+ self._data = []
+ self._retry = None
+
def decode(self, line: str) -> Optional[ServerSentEvent]:
# See: https://html.spec.whatwg.org/multipage/server-sent-events.html#event-stream-interpretation # noqa: E501
diff --git a/src/junction/core/jsonable_encoder.py b/src/junction/core/jsonable_encoder.py
index 5b0902e..f638cc9 100644
--- a/src/junction/core/jsonable_encoder.py
+++ b/src/junction/core/jsonable_encoder.py
@@ -15,6 +15,7 @@
from pathlib import PurePath
from types import GeneratorType
from typing import Any, Callable, Dict, List, Optional, Set, Union
+from urllib.parse import quote
import pydantic
from .datetime_utils import serialize_datetime
@@ -118,3 +119,15 @@ def encode_path_param(obj: Any) -> str:
if isinstance(obj, bool):
return "true" if obj else "false"
return str(jsonable_encoder(obj))
+
+
+def quote_path_param(obj: Any) -> str:
+ """Encode a value for use in a URL path segment, percent-encoding it.
+
+ Same as encode_path_param, except the result is percent-encoded so
+ that a value containing "/" or ".." cannot change which endpoint
+ the request resolves to.
+ """
+ if isinstance(obj, bool):
+ return "true" if obj else "false"
+ return quote(str(jsonable_encoder(obj)), safe="")
diff --git a/src/junction/core/pydantic_utilities.py b/src/junction/core/pydantic_utilities.py
index ed95da8..ce50d1d 100644
--- a/src/junction/core/pydantic_utilities.py
+++ b/src/junction/core/pydantic_utilities.py
@@ -296,10 +296,22 @@ def parse_sse_obj(sse: "ServerSentEvent", type_: Type[T]) -> T:
return parse_obj_as(type_, sse_event)
+_type_adapter_cache: Dict[int, Any] = {}
+
+
+def _get_type_adapter(type_: Type[Any]) -> Any:
+ key = id(type_)
+ adapter = _type_adapter_cache.get(key)
+ if adapter is None:
+ adapter = pydantic.TypeAdapter(type_) # type: ignore[attr-defined]
+ _type_adapter_cache[key] = adapter
+ return adapter
+
+
def parse_obj_as(type_: Type[T], object_: Any) -> T:
if IS_PYDANTIC_V2:
- adapter = pydantic.TypeAdapter(type_) # type: ignore[attr-defined]
- return adapter.validate_python(object_)
+ adapter = _get_type_adapter(type_)
+ return adapter.validate_python(object_) # type: ignore[no-any-return]
return pydantic.parse_obj_as(type_, object_)
diff --git a/src/junction/core/request_options.py b/src/junction/core/request_options.py
index 1b38804..caa6f66 100644
--- a/src/junction/core/request_options.py
+++ b/src/junction/core/request_options.py
@@ -14,7 +14,9 @@ class RequestOptions(typing.TypedDict, total=False):
This is used primarily as an optional final parameter for service functions.
Attributes:
- - timeout_in_seconds: int. The number of seconds to await an API call before timing out.
+ - timeout: float. The number of seconds to await an API call before timing out.
+
+ - timeout_in_seconds: int. Deprecated alias for `timeout`; both are in seconds. Prefer `timeout`.
- max_retries: int. The max number of retries to attempt if the API call fails.
@@ -27,9 +29,12 @@ class RequestOptions(typing.TypedDict, total=False):
- chunk_size: int. The size, in bytes, to process each chunk of data being streamed back within the response. This equates to leveraging `chunk_size` within `requests` or `httpx`, and is only leveraged for file downloads.
"""
+ timeout: NotRequired[float]
timeout_in_seconds: NotRequired[int]
max_retries: NotRequired[int]
additional_headers: NotRequired[typing.Dict[str, typing.Any]]
additional_query_parameters: NotRequired[typing.Dict[str, typing.Any]]
additional_body_parameters: NotRequired[typing.Dict[str, typing.Any]]
chunk_size: NotRequired[int]
+ stream_reconnection_enabled: NotRequired[bool]
+ max_stream_reconnection_attempts: NotRequired[int]
diff --git a/src/junction/core/serialization.py b/src/junction/core/serialization.py
index c36e865..1d753e2 100644
--- a/src/junction/core/serialization.py
+++ b/src/junction/core/serialization.py
@@ -26,6 +26,75 @@ def __init__(self, *, alias: str) -> None:
self.alias = alias
+# Resolving type hints (typing.get_type_hints) is expensive because it eval/compiles
+# forward-reference annotations. The result is constant for a given type, so we cache it.
+# This is critical for hot paths like SSE event parsing, where the same (often large
+# discriminated-union) type is converted on every single event.
+_type_hints_cache: typing.Dict[typing.Any, typing.Dict[str, typing.Any]] = {}
+
+
+def _get_cached_type_hints(expected_type: typing.Any) -> typing.Dict[str, typing.Any]:
+ try:
+ cached = _type_hints_cache.get(expected_type)
+ except TypeError:
+ # Unhashable type; resolve without caching.
+ return _resolve_type_hints(expected_type)
+ if cached is None:
+ cached = _resolve_type_hints(expected_type)
+ _type_hints_cache[expected_type] = cached
+ return cached
+
+
+def _resolve_type_hints(expected_type: typing.Any) -> typing.Dict[str, typing.Any]:
+ try:
+ return typing_extensions.get_type_hints(expected_type, include_extras=True)
+ except NameError:
+ # The type contains a circular reference, so we use the __annotations__ attribute directly.
+ return getattr(expected_type, "__annotations__", {})
+
+
+# Whether convert_and_respect_annotation_metadata can possibly rewrite anything for a given
+# annotation, i.e. whether any reachable model/TypedDict field carries a FieldMetadata alias.
+# This is constant per type, so we cache it and use it to short-circuit the recursive walk.
+_requires_conversion_cache: typing.Dict[typing.Any, bool] = {}
+
+
+def _requires_conversion(type_: typing.Any) -> bool:
+ try:
+ cached = _requires_conversion_cache.get(type_)
+ except TypeError:
+ # Unhashable annotation; compute without caching.
+ return _compute_requires_conversion(type_, set())
+ if cached is None:
+ cached = _compute_requires_conversion(type_, set())
+ _requires_conversion_cache[type_] = cached
+ return cached
+
+
+def _compute_requires_conversion(type_: typing.Any, seen: typing.Set[typing.Any]) -> bool:
+ clean_type = _remove_annotations(type_)
+
+ try:
+ if clean_type in seen:
+ return False
+ seen = seen | {clean_type}
+ except TypeError:
+ # Unhashable type; skip cycle tracking (the type graph is finite in practice).
+ pass
+
+ # Models / TypedDicts: a field alias here means we must dealias; otherwise recurse into fields.
+ if (inspect.isclass(clean_type) and issubclass(clean_type, pydantic.BaseModel)) or typing_extensions.is_typeddict(
+ clean_type
+ ):
+ annotations = _get_cached_type_hints(clean_type)
+ if _get_alias_to_field_name(annotations):
+ return True
+ return any(_compute_requires_conversion(hint, seen) for hint in annotations.values())
+
+ # Containers / unions: recurse into the type arguments (List/Set/Sequence/Dict/Union/etc.).
+ return any(_compute_requires_conversion(arg, seen) for arg in typing_extensions.get_args(clean_type))
+
+
def convert_and_respect_annotation_metadata(
*,
object_: typing.Any,
@@ -57,6 +126,13 @@ def convert_and_respect_annotation_metadata(
return None
if inner_type is None:
inner_type = annotation
+ # The only thing this function ever rewrites is keys that carry a FieldMetadata
+ # alias. If nothing in the (cached) type graph has such an alias, the conversion is
+ # a content-identity transform, so we can skip the entire recursive walk. This is
+ # the hot path for SSE streaming, where a large discriminated union would otherwise
+ # be traversed on every single event.
+ if not _requires_conversion(annotation):
+ return object_
clean_type = _remove_annotations(inner_type)
# Pydantic models
@@ -160,12 +236,7 @@ def _convert_mapping(
direction: typing.Literal["read", "write"],
) -> typing.Mapping[str, object]:
converted_object: typing.Dict[str, object] = {}
- try:
- annotations = typing_extensions.get_type_hints(expected_type, include_extras=True)
- except NameError:
- # The TypedDict contains a circular reference, so
- # we use the __annotations__ attribute directly.
- annotations = getattr(expected_type, "__annotations__", {})
+ annotations = _get_cached_type_hints(expected_type)
aliases_to_field_names = _get_alias_to_field_name(annotations)
for key, value in object_.items():
if direction == "read" and key in aliases_to_field_names:
@@ -221,12 +292,12 @@ def _remove_annotations(type_: typing.Any) -> typing.Any:
def get_alias_to_field_mapping(type_: typing.Any) -> typing.Dict[str, str]:
- annotations = typing_extensions.get_type_hints(type_, include_extras=True)
+ annotations = _get_cached_type_hints(type_)
return _get_alias_to_field_name(annotations)
def get_field_to_alias_mapping(type_: typing.Any) -> typing.Dict[str, str]:
- annotations = typing_extensions.get_type_hints(type_, include_extras=True)
+ annotations = _get_cached_type_hints(type_)
return _get_field_to_alias_name(annotations)
diff --git a/src/junction/electrocardiogram/client.py b/src/junction/electrocardiogram/client.py
index c0c26da..3960a09 100644
--- a/src/junction/electrocardiogram/client.py
+++ b/src/junction/electrocardiogram/client.py
@@ -40,8 +40,10 @@ def get(
user_id : str
start_date : str
+ Start date in YYYY-MM-DD
end_date : typing.Optional[str]
+ End date (inclusive) in YYYY-MM-DD
provider : typing.Optional[str]
Provider oura/strava etc
@@ -106,8 +108,10 @@ async def get(
user_id : str
start_date : str
+ Start date in YYYY-MM-DD
end_date : typing.Optional[str]
+ End date (inclusive) in YYYY-MM-DD
provider : typing.Optional[str]
Provider oura/strava etc
diff --git a/src/junction/electrocardiogram/raw_client.py b/src/junction/electrocardiogram/raw_client.py
index af2feab..2871cfa 100644
--- a/src/junction/electrocardiogram/raw_client.py
+++ b/src/junction/electrocardiogram/raw_client.py
@@ -37,8 +37,10 @@ def get(
user_id : str
start_date : str
+ Start date in YYYY-MM-DD
end_date : typing.Optional[str]
+ End date (inclusive) in YYYY-MM-DD
provider : typing.Optional[str]
Provider oura/strava etc
@@ -113,8 +115,10 @@ async def get(
user_id : str
start_date : str
+ Start date in YYYY-MM-DD
end_date : typing.Optional[str]
+ End date (inclusive) in YYYY-MM-DD
provider : typing.Optional[str]
Provider oura/strava etc
diff --git a/src/junction/lab_tests/client.py b/src/junction/lab_tests/client.py
index b56b518..a77839f 100644
--- a/src/junction/lab_tests/client.py
+++ b/src/junction/lab_tests/client.py
@@ -21,8 +21,11 @@
from ..types.client_facing_marker import ClientFacingMarker
from ..types.client_facing_order import ClientFacingOrder
from ..types.consent import Consent
+from ..types.create_unmatched_result_test_response import CreateUnmatchedResultTestResponse
from ..types.get_markers_response import GetMarkersResponse
from ..types.get_orders_response import GetOrdersResponse
+from ..types.get_unmatched_result_response import GetUnmatchedResultResponse
+from ..types.get_unmatched_result_test_response import GetUnmatchedResultTestResponse
from ..types.health_insurance_create_request import HealthInsuranceCreateRequest
from ..types.interpretation import Interpretation
from ..types.lab_location_capability import LabLocationCapability
@@ -33,6 +36,10 @@
from ..types.lab_test_resources_response import LabTestResourcesResponse
from ..types.lab_test_status import LabTestStatus
from ..types.labs import Labs
+from ..types.list_unmatched_result_response import ListUnmatchedResultResponse
+from ..types.list_unmatched_result_test_cases_response import ListUnmatchedResultTestCasesResponse
+from ..types.match_decision_code import MatchDecisionCode
+from ..types.match_review_status_filter import MatchReviewStatusFilter
from ..types.order_activation_type import OrderActivationType
from ..types.order_low_level_status import OrderLowLevelStatus
from ..types.order_set_request import OrderSetRequest
@@ -43,7 +50,12 @@
from ..types.physician_create_request import PhysicianCreateRequest
from ..types.post_order_response import PostOrderResponse
from ..types.psc_info import PscInfo
+from ..types.result_status import ResultStatus
from ..types.simulation_flags import SimulationFlags
+from ..types.unmatched_result import UnmatchedResult
+from ..types.unmatched_result_resolution_action import UnmatchedResultResolutionAction
+from ..types.unmatched_result_test_case import UnmatchedResultTestCase
+from ..types.unmatched_result_test_order_source import UnmatchedResultTestOrderSource
from ..types.us_address import UsAddress
from ..types.validate_icd_codes_response import ValidateIcdCodesResponse
from .raw_client import AsyncRawLabTestsClient, RawLabTestsClient
@@ -325,6 +337,7 @@ def get_markers(
lab_slug: typing.Optional[str] = None,
name: typing.Optional[str] = None,
a_la_carte_enabled: typing.Optional[bool] = None,
+ include_pricing: typing.Optional[bool] = None,
lab_account_id: typing.Optional[str] = None,
page: typing.Optional[int] = None,
size: typing.Optional[int] = None,
@@ -346,6 +359,8 @@ def get_markers(
a_la_carte_enabled : typing.Optional[bool]
+ include_pricing : typing.Optional[bool]
+
lab_account_id : typing.Optional[str]
The lab account ID. This lab account is used to determine the availability of markers and lab tests.
@@ -373,6 +388,7 @@ def get_markers(
lab_slug="lab_slug",
name="name",
a_la_carte_enabled=True,
+ include_pricing=True,
lab_account_id="lab_account_id",
page=1,
size=1,
@@ -383,6 +399,7 @@ def get_markers(
lab_slug=lab_slug,
name=name,
a_la_carte_enabled=a_la_carte_enabled,
+ include_pricing=include_pricing,
lab_account_id=lab_account_id,
page=page,
size=size,
@@ -570,6 +587,8 @@ def get_paginated(
*,
lab_test_limit: typing.Optional[int] = None,
next_cursor: typing.Optional[str] = None,
+ include_pricing: typing.Optional[bool] = None,
+ lab_account_id: typing.Optional[str] = None,
generation_method: typing.Optional[LabTestGenerationMethodFilter] = None,
lab_slug: typing.Optional[str] = None,
collection_method: typing.Optional[LabTestCollectionMethod] = None,
@@ -589,6 +608,12 @@ def get_paginated(
lab_test_limit : typing.Optional[int]
next_cursor : typing.Optional[str]
+ The cursor for fetching the next page, or `null` to fetch the first page.
+
+ include_pricing : typing.Optional[bool]
+
+ lab_account_id : typing.Optional[str]
+ The lab account ID. This lab account is used to determine the availability of markers and lab tests.
generation_method : typing.Optional[LabTestGenerationMethodFilter]
Filter on whether auto-generated lab tests created by Vital, manually created lab tests, or all lab tests should be returned.
@@ -642,6 +667,8 @@ def get_paginated(
client.lab_tests.get_paginated(
lab_test_limit=1,
next_cursor="next_cursor",
+ include_pricing=True,
+ lab_account_id="lab_account_id",
generation_method=LabTestGenerationMethodFilter.AUTO,
lab_slug="lab_slug",
collection_method=LabTestCollectionMethod.TESTKIT,
@@ -656,6 +683,8 @@ def get_paginated(
_response = self._raw_client.get_paginated(
lab_test_limit=lab_test_limit,
next_cursor=next_cursor,
+ include_pricing=include_pricing,
+ lab_account_id=lab_account_id,
generation_method=generation_method,
lab_slug=lab_slug,
collection_method=collection_method,
@@ -2359,6 +2388,331 @@ def update_on_site_collection_order_draw_completed(
)
return _response.data
+ def list_unmatched_result_test_cases(
+ self, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> ListUnmatchedResultTestCasesResponse:
+ """
+ Parameters
+ ----------
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ ListUnmatchedResultTestCasesResponse
+ Successful Response
+
+ Examples
+ --------
+ from junction import Junction
+
+ client = Junction(
+ api_key="YOUR_API_KEY",
+ )
+ client.lab_tests.list_unmatched_result_test_cases()
+ """
+ _response = self._raw_client.list_unmatched_result_test_cases(request_options=request_options)
+ return _response.data
+
+ def create_unmatched_result_test(
+ self,
+ *,
+ idempotency_key: str,
+ case: UnmatchedResultTestCase,
+ order_source: UnmatchedResultTestOrderSource,
+ orders: typing.Optional[typing.Dict[str, typing.Optional[str]]] = OMIT,
+ result_status: typing.Optional[ResultStatus] = OMIT,
+ interpretation: typing.Optional[Interpretation] = OMIT,
+ lab_test_id: typing.Optional[str] = OMIT,
+ wrong_lab_test_id: typing.Optional[str] = OMIT,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> CreateUnmatchedResultTestResponse:
+ """
+ Parameters
+ ----------
+ idempotency_key : str
+
+ case : UnmatchedResultTestCase
+ ℹ️ This enum is non-exhaustive.
+
+ order_source : UnmatchedResultTestOrderSource
+ ℹ️ This enum is non-exhaustive.
+
+ orders : typing.Optional[typing.Dict[str, typing.Optional[str]]]
+
+ result_status : typing.Optional[ResultStatus]
+ ℹ️ This enum is non-exhaustive.
+
+ interpretation : typing.Optional[Interpretation]
+ ℹ️ This enum is non-exhaustive.
+
+ lab_test_id : typing.Optional[str]
+
+ wrong_lab_test_id : typing.Optional[str]
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ CreateUnmatchedResultTestResponse
+ Successful Response
+
+ Examples
+ --------
+ from junction import (
+ Junction,
+ UnmatchedResultTestCase,
+ UnmatchedResultTestOrderSource,
+ )
+
+ client = Junction(
+ api_key="YOUR_API_KEY",
+ )
+ client.lab_tests.create_unmatched_result_test(
+ idempotency_key="X-Idempotency-Key",
+ case=UnmatchedResultTestCase.MATCH_COMPLETED,
+ order_source=UnmatchedResultTestOrderSource.MANAGED,
+ )
+ """
+ _response = self._raw_client.create_unmatched_result_test(
+ idempotency_key=idempotency_key,
+ case=case,
+ order_source=order_source,
+ orders=orders,
+ result_status=result_status,
+ interpretation=interpretation,
+ lab_test_id=lab_test_id,
+ wrong_lab_test_id=wrong_lab_test_id,
+ request_options=request_options,
+ )
+ return _response.data
+
+ def get_unmatched_result_test(
+ self, run_id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> GetUnmatchedResultTestResponse:
+ """
+ Parameters
+ ----------
+ run_id : str
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ GetUnmatchedResultTestResponse
+ Successful Response
+
+ Examples
+ --------
+ from junction import Junction
+
+ client = Junction(
+ api_key="YOUR_API_KEY",
+ )
+ client.lab_tests.get_unmatched_result_test(
+ run_id="run_id",
+ )
+ """
+ _response = self._raw_client.get_unmatched_result_test(run_id, request_options=request_options)
+ return _response.data
+
+ def list_unmatched_results(
+ self,
+ *,
+ limit: typing.Optional[int] = None,
+ next_cursor: typing.Optional[str] = None,
+ decision_code: typing.Optional[MatchDecisionCode] = None,
+ lab_slug: typing.Optional[str] = None,
+ status: typing.Optional[MatchReviewStatusFilter] = None,
+ created_at_start: typing.Optional[str] = None,
+ created_at_end: typing.Optional[str] = None,
+ search_input: typing.Optional[str] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> ListUnmatchedResultResponse:
+ """
+ Parameters
+ ----------
+ limit : typing.Optional[int]
+
+ next_cursor : typing.Optional[str]
+ The cursor for fetching the next page, or `null` to fetch the first page.
+
+ decision_code : typing.Optional[MatchDecisionCode]
+ Filter by match decision code.
+
+ lab_slug : typing.Optional[str]
+ Filter by lab slug (e.g. `labcorp`, `quest`).
+
+ status : typing.Optional[MatchReviewStatusFilter]
+ Filter by review status. `pending_customer_review` returns items awaiting your action; `pending_ops_review` returns items you have escalated for review.
+
+ created_at_start : typing.Optional[str]
+ Filter by result receipt date on or after this date (UTC, inclusive, YYYY-MM-DD).
+
+ created_at_end : typing.Optional[str]
+ Filter by result receipt date on or before this date (UTC, inclusive, YYYY-MM-DD).
+
+ search_input : typing.Optional[str]
+ Search by patient first name, last name, or date of birth (e.g. `Alice`, `Smith`, or `1990-01-15`).
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ ListUnmatchedResultResponse
+ Successful Response
+
+ Examples
+ --------
+ from junction import Junction, MatchDecisionCode, MatchReviewStatusFilter
+
+ client = Junction(
+ api_key="YOUR_API_KEY",
+ )
+ client.lab_tests.list_unmatched_results(
+ limit=1,
+ next_cursor="next_cursor",
+ decision_code=MatchDecisionCode.MATCH_SAMPLE_ID,
+ lab_slug="lab_slug",
+ status=MatchReviewStatusFilter.PENDING_CUSTOMER_REVIEW,
+ created_at_start="created_at_start",
+ created_at_end="created_at_end",
+ search_input="search_input",
+ )
+ """
+ _response = self._raw_client.list_unmatched_results(
+ limit=limit,
+ next_cursor=next_cursor,
+ decision_code=decision_code,
+ lab_slug=lab_slug,
+ status=status,
+ created_at_start=created_at_start,
+ created_at_end=created_at_end,
+ search_input=search_input,
+ request_options=request_options,
+ )
+ return _response.data
+
+ def get_unmatched_result(
+ self, raw_result_id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> GetUnmatchedResultResponse:
+ """
+ Parameters
+ ----------
+ raw_result_id : str
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ GetUnmatchedResultResponse
+ Successful Response
+
+ Examples
+ --------
+ from junction import Junction
+
+ client = Junction(
+ api_key="YOUR_API_KEY",
+ )
+ client.lab_tests.get_unmatched_result(
+ raw_result_id="raw_result_id",
+ )
+ """
+ _response = self._raw_client.get_unmatched_result(raw_result_id, request_options=request_options)
+ return _response.data
+
+ def accept_unmatched_result(
+ self,
+ raw_result_id: str,
+ *,
+ user_id: typing.Optional[str] = OMIT,
+ order_id: typing.Optional[str] = OMIT,
+ note: typing.Optional[str] = OMIT,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> ClientFacingOrder:
+ """
+ Parameters
+ ----------
+ raw_result_id : str
+
+ user_id : typing.Optional[str]
+
+ order_id : typing.Optional[str]
+
+ note : typing.Optional[str]
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ ClientFacingOrder
+ Successful Response
+
+ Examples
+ --------
+ from junction import Junction
+
+ client = Junction(
+ api_key="YOUR_API_KEY",
+ )
+ client.lab_tests.accept_unmatched_result(
+ raw_result_id="raw_result_id",
+ )
+ """
+ _response = self._raw_client.accept_unmatched_result(
+ raw_result_id, user_id=user_id, order_id=order_id, note=note, request_options=request_options
+ )
+ return _response.data
+
+ def resolve_unmatched_result(
+ self,
+ raw_result_id: str,
+ *,
+ action: UnmatchedResultResolutionAction,
+ note: typing.Optional[str] = OMIT,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> UnmatchedResult:
+ """
+ Parameters
+ ----------
+ raw_result_id : str
+
+ action : UnmatchedResultResolutionAction
+ ℹ️ This enum is non-exhaustive.
+
+ note : typing.Optional[str]
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ UnmatchedResult
+ Successful Response
+
+ Examples
+ --------
+ from junction import Junction, UnmatchedResultResolutionAction
+
+ client = Junction(
+ api_key="YOUR_API_KEY",
+ )
+ client.lab_tests.resolve_unmatched_result(
+ raw_result_id="raw_result_id",
+ action=UnmatchedResultResolutionAction.REJECT,
+ )
+ """
+ _response = self._raw_client.resolve_unmatched_result(
+ raw_result_id, action=action, note=note, request_options=request_options
+ )
+ return _response.data
+
def validate_icd_codes(
self, *, codes: typing.Sequence[str], request_options: typing.Optional[RequestOptions] = None
) -> ValidateIcdCodesResponse:
@@ -2689,6 +3043,7 @@ async def get_markers(
lab_slug: typing.Optional[str] = None,
name: typing.Optional[str] = None,
a_la_carte_enabled: typing.Optional[bool] = None,
+ include_pricing: typing.Optional[bool] = None,
lab_account_id: typing.Optional[str] = None,
page: typing.Optional[int] = None,
size: typing.Optional[int] = None,
@@ -2710,6 +3065,8 @@ async def get_markers(
a_la_carte_enabled : typing.Optional[bool]
+ include_pricing : typing.Optional[bool]
+
lab_account_id : typing.Optional[str]
The lab account ID. This lab account is used to determine the availability of markers and lab tests.
@@ -2742,6 +3099,7 @@ async def main() -> None:
lab_slug="lab_slug",
name="name",
a_la_carte_enabled=True,
+ include_pricing=True,
lab_account_id="lab_account_id",
page=1,
size=1,
@@ -2755,6 +3113,7 @@ async def main() -> None:
lab_slug=lab_slug,
name=name,
a_la_carte_enabled=a_la_carte_enabled,
+ include_pricing=include_pricing,
lab_account_id=lab_account_id,
page=page,
size=size,
@@ -2976,6 +3335,8 @@ async def get_paginated(
*,
lab_test_limit: typing.Optional[int] = None,
next_cursor: typing.Optional[str] = None,
+ include_pricing: typing.Optional[bool] = None,
+ lab_account_id: typing.Optional[str] = None,
generation_method: typing.Optional[LabTestGenerationMethodFilter] = None,
lab_slug: typing.Optional[str] = None,
collection_method: typing.Optional[LabTestCollectionMethod] = None,
@@ -2995,6 +3356,12 @@ async def get_paginated(
lab_test_limit : typing.Optional[int]
next_cursor : typing.Optional[str]
+ The cursor for fetching the next page, or `null` to fetch the first page.
+
+ include_pricing : typing.Optional[bool]
+
+ lab_account_id : typing.Optional[str]
+ The lab account ID. This lab account is used to determine the availability of markers and lab tests.
generation_method : typing.Optional[LabTestGenerationMethodFilter]
Filter on whether auto-generated lab tests created by Vital, manually created lab tests, or all lab tests should be returned.
@@ -3053,6 +3420,8 @@ async def main() -> None:
await client.lab_tests.get_paginated(
lab_test_limit=1,
next_cursor="next_cursor",
+ include_pricing=True,
+ lab_account_id="lab_account_id",
generation_method=LabTestGenerationMethodFilter.AUTO,
lab_slug="lab_slug",
collection_method=LabTestCollectionMethod.TESTKIT,
@@ -3070,6 +3439,8 @@ async def main() -> None:
_response = await self._raw_client.get_paginated(
lab_test_limit=lab_test_limit,
next_cursor=next_cursor,
+ include_pricing=include_pricing,
+ lab_account_id=lab_account_id,
generation_method=generation_method,
lab_slug=lab_slug,
collection_method=collection_method,
@@ -5041,6 +5412,387 @@ async def main() -> None:
)
return _response.data
+ async def list_unmatched_result_test_cases(
+ self, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> ListUnmatchedResultTestCasesResponse:
+ """
+ Parameters
+ ----------
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ ListUnmatchedResultTestCasesResponse
+ Successful Response
+
+ Examples
+ --------
+ import asyncio
+
+ from junction import AsyncJunction
+
+ client = AsyncJunction(
+ api_key="YOUR_API_KEY",
+ )
+
+
+ async def main() -> None:
+ await client.lab_tests.list_unmatched_result_test_cases()
+
+
+ asyncio.run(main())
+ """
+ _response = await self._raw_client.list_unmatched_result_test_cases(request_options=request_options)
+ return _response.data
+
+ async def create_unmatched_result_test(
+ self,
+ *,
+ idempotency_key: str,
+ case: UnmatchedResultTestCase,
+ order_source: UnmatchedResultTestOrderSource,
+ orders: typing.Optional[typing.Dict[str, typing.Optional[str]]] = OMIT,
+ result_status: typing.Optional[ResultStatus] = OMIT,
+ interpretation: typing.Optional[Interpretation] = OMIT,
+ lab_test_id: typing.Optional[str] = OMIT,
+ wrong_lab_test_id: typing.Optional[str] = OMIT,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> CreateUnmatchedResultTestResponse:
+ """
+ Parameters
+ ----------
+ idempotency_key : str
+
+ case : UnmatchedResultTestCase
+ ℹ️ This enum is non-exhaustive.
+
+ order_source : UnmatchedResultTestOrderSource
+ ℹ️ This enum is non-exhaustive.
+
+ orders : typing.Optional[typing.Dict[str, typing.Optional[str]]]
+
+ result_status : typing.Optional[ResultStatus]
+ ℹ️ This enum is non-exhaustive.
+
+ interpretation : typing.Optional[Interpretation]
+ ℹ️ This enum is non-exhaustive.
+
+ lab_test_id : typing.Optional[str]
+
+ wrong_lab_test_id : typing.Optional[str]
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ CreateUnmatchedResultTestResponse
+ Successful Response
+
+ Examples
+ --------
+ import asyncio
+
+ from junction import (
+ AsyncJunction,
+ UnmatchedResultTestCase,
+ UnmatchedResultTestOrderSource,
+ )
+
+ client = AsyncJunction(
+ api_key="YOUR_API_KEY",
+ )
+
+
+ async def main() -> None:
+ await client.lab_tests.create_unmatched_result_test(
+ idempotency_key="X-Idempotency-Key",
+ case=UnmatchedResultTestCase.MATCH_COMPLETED,
+ order_source=UnmatchedResultTestOrderSource.MANAGED,
+ )
+
+
+ asyncio.run(main())
+ """
+ _response = await self._raw_client.create_unmatched_result_test(
+ idempotency_key=idempotency_key,
+ case=case,
+ order_source=order_source,
+ orders=orders,
+ result_status=result_status,
+ interpretation=interpretation,
+ lab_test_id=lab_test_id,
+ wrong_lab_test_id=wrong_lab_test_id,
+ request_options=request_options,
+ )
+ return _response.data
+
+ async def get_unmatched_result_test(
+ self, run_id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> GetUnmatchedResultTestResponse:
+ """
+ Parameters
+ ----------
+ run_id : str
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ GetUnmatchedResultTestResponse
+ Successful Response
+
+ Examples
+ --------
+ import asyncio
+
+ from junction import AsyncJunction
+
+ client = AsyncJunction(
+ api_key="YOUR_API_KEY",
+ )
+
+
+ async def main() -> None:
+ await client.lab_tests.get_unmatched_result_test(
+ run_id="run_id",
+ )
+
+
+ asyncio.run(main())
+ """
+ _response = await self._raw_client.get_unmatched_result_test(run_id, request_options=request_options)
+ return _response.data
+
+ async def list_unmatched_results(
+ self,
+ *,
+ limit: typing.Optional[int] = None,
+ next_cursor: typing.Optional[str] = None,
+ decision_code: typing.Optional[MatchDecisionCode] = None,
+ lab_slug: typing.Optional[str] = None,
+ status: typing.Optional[MatchReviewStatusFilter] = None,
+ created_at_start: typing.Optional[str] = None,
+ created_at_end: typing.Optional[str] = None,
+ search_input: typing.Optional[str] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> ListUnmatchedResultResponse:
+ """
+ Parameters
+ ----------
+ limit : typing.Optional[int]
+
+ next_cursor : typing.Optional[str]
+ The cursor for fetching the next page, or `null` to fetch the first page.
+
+ decision_code : typing.Optional[MatchDecisionCode]
+ Filter by match decision code.
+
+ lab_slug : typing.Optional[str]
+ Filter by lab slug (e.g. `labcorp`, `quest`).
+
+ status : typing.Optional[MatchReviewStatusFilter]
+ Filter by review status. `pending_customer_review` returns items awaiting your action; `pending_ops_review` returns items you have escalated for review.
+
+ created_at_start : typing.Optional[str]
+ Filter by result receipt date on or after this date (UTC, inclusive, YYYY-MM-DD).
+
+ created_at_end : typing.Optional[str]
+ Filter by result receipt date on or before this date (UTC, inclusive, YYYY-MM-DD).
+
+ search_input : typing.Optional[str]
+ Search by patient first name, last name, or date of birth (e.g. `Alice`, `Smith`, or `1990-01-15`).
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ ListUnmatchedResultResponse
+ Successful Response
+
+ Examples
+ --------
+ import asyncio
+
+ from junction import AsyncJunction, MatchDecisionCode, MatchReviewStatusFilter
+
+ client = AsyncJunction(
+ api_key="YOUR_API_KEY",
+ )
+
+
+ async def main() -> None:
+ await client.lab_tests.list_unmatched_results(
+ limit=1,
+ next_cursor="next_cursor",
+ decision_code=MatchDecisionCode.MATCH_SAMPLE_ID,
+ lab_slug="lab_slug",
+ status=MatchReviewStatusFilter.PENDING_CUSTOMER_REVIEW,
+ created_at_start="created_at_start",
+ created_at_end="created_at_end",
+ search_input="search_input",
+ )
+
+
+ asyncio.run(main())
+ """
+ _response = await self._raw_client.list_unmatched_results(
+ limit=limit,
+ next_cursor=next_cursor,
+ decision_code=decision_code,
+ lab_slug=lab_slug,
+ status=status,
+ created_at_start=created_at_start,
+ created_at_end=created_at_end,
+ search_input=search_input,
+ request_options=request_options,
+ )
+ return _response.data
+
+ async def get_unmatched_result(
+ self, raw_result_id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> GetUnmatchedResultResponse:
+ """
+ Parameters
+ ----------
+ raw_result_id : str
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ GetUnmatchedResultResponse
+ Successful Response
+
+ Examples
+ --------
+ import asyncio
+
+ from junction import AsyncJunction
+
+ client = AsyncJunction(
+ api_key="YOUR_API_KEY",
+ )
+
+
+ async def main() -> None:
+ await client.lab_tests.get_unmatched_result(
+ raw_result_id="raw_result_id",
+ )
+
+
+ asyncio.run(main())
+ """
+ _response = await self._raw_client.get_unmatched_result(raw_result_id, request_options=request_options)
+ return _response.data
+
+ async def accept_unmatched_result(
+ self,
+ raw_result_id: str,
+ *,
+ user_id: typing.Optional[str] = OMIT,
+ order_id: typing.Optional[str] = OMIT,
+ note: typing.Optional[str] = OMIT,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> ClientFacingOrder:
+ """
+ Parameters
+ ----------
+ raw_result_id : str
+
+ user_id : typing.Optional[str]
+
+ order_id : typing.Optional[str]
+
+ note : typing.Optional[str]
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ ClientFacingOrder
+ Successful Response
+
+ Examples
+ --------
+ import asyncio
+
+ from junction import AsyncJunction
+
+ client = AsyncJunction(
+ api_key="YOUR_API_KEY",
+ )
+
+
+ async def main() -> None:
+ await client.lab_tests.accept_unmatched_result(
+ raw_result_id="raw_result_id",
+ )
+
+
+ asyncio.run(main())
+ """
+ _response = await self._raw_client.accept_unmatched_result(
+ raw_result_id, user_id=user_id, order_id=order_id, note=note, request_options=request_options
+ )
+ return _response.data
+
+ async def resolve_unmatched_result(
+ self,
+ raw_result_id: str,
+ *,
+ action: UnmatchedResultResolutionAction,
+ note: typing.Optional[str] = OMIT,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> UnmatchedResult:
+ """
+ Parameters
+ ----------
+ raw_result_id : str
+
+ action : UnmatchedResultResolutionAction
+ ℹ️ This enum is non-exhaustive.
+
+ note : typing.Optional[str]
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ UnmatchedResult
+ Successful Response
+
+ Examples
+ --------
+ import asyncio
+
+ from junction import AsyncJunction, UnmatchedResultResolutionAction
+
+ client = AsyncJunction(
+ api_key="YOUR_API_KEY",
+ )
+
+
+ async def main() -> None:
+ await client.lab_tests.resolve_unmatched_result(
+ raw_result_id="raw_result_id",
+ action=UnmatchedResultResolutionAction.REJECT,
+ )
+
+
+ asyncio.run(main())
+ """
+ _response = await self._raw_client.resolve_unmatched_result(
+ raw_result_id, action=action, note=note, request_options=request_options
+ )
+ return _response.data
+
async def validate_icd_codes(
self, *, codes: typing.Sequence[str], request_options: typing.Optional[RequestOptions] = None
) -> ValidateIcdCodesResponse:
diff --git a/src/junction/lab_tests/raw_client.py b/src/junction/lab_tests/raw_client.py
index bd3e7c4..afad5ee 100644
--- a/src/junction/lab_tests/raw_client.py
+++ b/src/junction/lab_tests/raw_client.py
@@ -31,8 +31,11 @@
from ..types.client_facing_marker import ClientFacingMarker
from ..types.client_facing_order import ClientFacingOrder
from ..types.consent import Consent
+from ..types.create_unmatched_result_test_response import CreateUnmatchedResultTestResponse
from ..types.get_markers_response import GetMarkersResponse
from ..types.get_orders_response import GetOrdersResponse
+from ..types.get_unmatched_result_response import GetUnmatchedResultResponse
+from ..types.get_unmatched_result_test_response import GetUnmatchedResultTestResponse
from ..types.health_insurance_create_request import HealthInsuranceCreateRequest
from ..types.http_validation_error import HttpValidationError
from ..types.interpretation import Interpretation
@@ -44,6 +47,10 @@
from ..types.lab_test_resources_response import LabTestResourcesResponse
from ..types.lab_test_status import LabTestStatus
from ..types.labs import Labs
+from ..types.list_unmatched_result_response import ListUnmatchedResultResponse
+from ..types.list_unmatched_result_test_cases_response import ListUnmatchedResultTestCasesResponse
+from ..types.match_decision_code import MatchDecisionCode
+from ..types.match_review_status_filter import MatchReviewStatusFilter
from ..types.not_found_error_body import NotFoundErrorBody
from ..types.order_activation_type import OrderActivationType
from ..types.order_low_level_status import OrderLowLevelStatus
@@ -55,7 +62,12 @@
from ..types.physician_create_request import PhysicianCreateRequest
from ..types.post_order_response import PostOrderResponse
from ..types.psc_info import PscInfo
+from ..types.result_status import ResultStatus
from ..types.simulation_flags import SimulationFlags
+from ..types.unmatched_result import UnmatchedResult
+from ..types.unmatched_result_resolution_action import UnmatchedResultResolutionAction
+from ..types.unmatched_result_test_case import UnmatchedResultTestCase
+from ..types.unmatched_result_test_order_source import UnmatchedResultTestOrderSource
from ..types.us_address import UsAddress
from ..types.validate_icd_codes_response import ValidateIcdCodesResponse
from .types.get_lab_tests_request_order_direction import GetLabTestsRequestOrderDirection
@@ -401,6 +413,7 @@ def get_markers(
lab_slug: typing.Optional[str] = None,
name: typing.Optional[str] = None,
a_la_carte_enabled: typing.Optional[bool] = None,
+ include_pricing: typing.Optional[bool] = None,
lab_account_id: typing.Optional[str] = None,
page: typing.Optional[int] = None,
size: typing.Optional[int] = None,
@@ -422,6 +435,8 @@ def get_markers(
a_la_carte_enabled : typing.Optional[bool]
+ include_pricing : typing.Optional[bool]
+
lab_account_id : typing.Optional[str]
The lab account ID. This lab account is used to determine the availability of markers and lab tests.
@@ -445,6 +460,7 @@ def get_markers(
"lab_slug": lab_slug,
"name": name,
"a_la_carte_enabled": a_la_carte_enabled,
+ "include_pricing": include_pricing,
"lab_account_id": lab_account_id,
"page": page,
"size": size,
@@ -742,6 +758,8 @@ def get_paginated(
*,
lab_test_limit: typing.Optional[int] = None,
next_cursor: typing.Optional[str] = None,
+ include_pricing: typing.Optional[bool] = None,
+ lab_account_id: typing.Optional[str] = None,
generation_method: typing.Optional[LabTestGenerationMethodFilter] = None,
lab_slug: typing.Optional[str] = None,
collection_method: typing.Optional[LabTestCollectionMethod] = None,
@@ -761,6 +779,12 @@ def get_paginated(
lab_test_limit : typing.Optional[int]
next_cursor : typing.Optional[str]
+ The cursor for fetching the next page, or `null` to fetch the first page.
+
+ include_pricing : typing.Optional[bool]
+
+ lab_account_id : typing.Optional[str]
+ The lab account ID. This lab account is used to determine the availability of markers and lab tests.
generation_method : typing.Optional[LabTestGenerationMethodFilter]
Filter on whether auto-generated lab tests created by Vital, manually created lab tests, or all lab tests should be returned.
@@ -801,6 +825,8 @@ def get_paginated(
params={
"lab_test_limit": lab_test_limit,
"next_cursor": next_cursor,
+ "include_pricing": include_pricing,
+ "lab_account_id": lab_account_id,
"generation_method": generation_method,
"lab_slug": lab_slug,
"collection_method": collection_method,
@@ -3178,30 +3204,103 @@ def update_on_site_collection_order_draw_completed(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- def validate_icd_codes(
- self, *, codes: typing.Sequence[str], request_options: typing.Optional[RequestOptions] = None
- ) -> HttpResponse[ValidateIcdCodesResponse]:
+ def list_unmatched_result_test_cases(
+ self, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> HttpResponse[ListUnmatchedResultTestCasesResponse]:
"""
Parameters
----------
- codes : typing.Sequence[str]
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ HttpResponse[ListUnmatchedResultTestCasesResponse]
+ Successful Response
+ """
+ _response = self._client_wrapper.httpx_client.request(
+ "v3/unmatched_result_test/case",
+ method="GET",
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ ListUnmatchedResultTestCasesResponse,
+ parse_obj_as(
+ type_=ListUnmatchedResultTestCasesResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return HttpResponse(response=_response, data=_data)
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ def create_unmatched_result_test(
+ self,
+ *,
+ idempotency_key: str,
+ case: UnmatchedResultTestCase,
+ order_source: UnmatchedResultTestOrderSource,
+ orders: typing.Optional[typing.Dict[str, typing.Optional[str]]] = OMIT,
+ result_status: typing.Optional[ResultStatus] = OMIT,
+ interpretation: typing.Optional[Interpretation] = OMIT,
+ lab_test_id: typing.Optional[str] = OMIT,
+ wrong_lab_test_id: typing.Optional[str] = OMIT,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> HttpResponse[CreateUnmatchedResultTestResponse]:
+ """
+ Parameters
+ ----------
+ idempotency_key : str
+
+ case : UnmatchedResultTestCase
+ ℹ️ This enum is non-exhaustive.
+
+ order_source : UnmatchedResultTestOrderSource
+ ℹ️ This enum is non-exhaustive.
+
+ orders : typing.Optional[typing.Dict[str, typing.Optional[str]]]
+
+ result_status : typing.Optional[ResultStatus]
+ ℹ️ This enum is non-exhaustive.
+
+ interpretation : typing.Optional[Interpretation]
+ ℹ️ This enum is non-exhaustive.
+
+ lab_test_id : typing.Optional[str]
+
+ wrong_lab_test_id : typing.Optional[str]
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- HttpResponse[ValidateIcdCodesResponse]
+ HttpResponse[CreateUnmatchedResultTestResponse]
Successful Response
"""
_response = self._client_wrapper.httpx_client.request(
- "v3/insurance/validate_icd_codes",
+ "v3/unmatched_result_test",
method="POST",
json={
- "codes": codes,
+ "case": case,
+ "order_source": order_source,
+ "orders": orders,
+ "result_status": result_status,
+ "interpretation": interpretation,
+ "lab_test_id": lab_test_id,
+ "wrong_lab_test_id": wrong_lab_test_id,
},
headers={
"content-type": "application/json",
+ "X-Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None,
},
request_options=request_options,
omit=OMIT,
@@ -3209,9 +3308,9 @@ def validate_icd_codes(
try:
if 200 <= _response.status_code < 300:
_data = typing.cast(
- ValidateIcdCodesResponse,
+ CreateUnmatchedResultTestResponse,
parse_obj_as(
- type_=ValidateIcdCodesResponse, # type: ignore
+ type_=CreateUnmatchedResultTestResponse, # type: ignore
object_=_response.json(),
),
)
@@ -3236,89 +3335,37 @@ def validate_icd_codes(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
-
-class AsyncRawLabTestsClient:
- def __init__(self, *, client_wrapper: AsyncClientWrapper):
- self._client_wrapper = client_wrapper
-
- async def get(
- self,
- *,
- generation_method: typing.Optional[LabTestGenerationMethodFilter] = None,
- lab_slug: typing.Optional[str] = None,
- collection_method: typing.Optional[LabTestCollectionMethod] = None,
- status: typing.Optional[LabTestStatus] = None,
- marker_ids: typing.Optional[typing.Union[int, typing.Sequence[int]]] = None,
- provider_ids: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None,
- name: typing.Optional[str] = None,
- order_key: typing.Optional[GetLabTestsRequestOrderKey] = None,
- order_direction: typing.Optional[GetLabTestsRequestOrderDirection] = None,
- request_options: typing.Optional[RequestOptions] = None,
- ) -> AsyncHttpResponse[typing.List[ClientFacingLabTest]]:
+ def get_unmatched_result_test(
+ self, run_id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> HttpResponse[GetUnmatchedResultTestResponse]:
"""
- GET all the lab tests the team has access to.
-
Parameters
----------
- generation_method : typing.Optional[LabTestGenerationMethodFilter]
- Filter on whether auto-generated lab tests created by Vital, manually created lab tests, or all lab tests should be returned.
-
- lab_slug : typing.Optional[str]
- Filter by the slug of the lab for these lab tests.
-
- collection_method : typing.Optional[LabTestCollectionMethod]
- Filter by the collection method for these lab tests.
-
- status : typing.Optional[LabTestStatus]
- Filter by the status of these lab tests.
-
- marker_ids : typing.Optional[typing.Union[int, typing.Sequence[int]]]
- Filter to only include lab tests containing these marker IDs.
-
- provider_ids : typing.Optional[typing.Union[str, typing.Sequence[str]]]
- Filter to only include lab tests containing these provider IDs.
-
- name : typing.Optional[str]
- Filter by the name of the lab test (a case-insensitive substring search).
-
- order_key : typing.Optional[GetLabTestsRequestOrderKey]
-
- order_direction : typing.Optional[GetLabTestsRequestOrderDirection]
+ run_id : str
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- AsyncHttpResponse[typing.List[ClientFacingLabTest]]
+ HttpResponse[GetUnmatchedResultTestResponse]
Successful Response
"""
- _response = await self._client_wrapper.httpx_client.request(
- "v3/lab_tests",
+ _response = self._client_wrapper.httpx_client.request(
+ f"v3/unmatched_result_test/{encode_path_param(run_id)}",
method="GET",
- params={
- "generation_method": generation_method,
- "lab_slug": lab_slug,
- "collection_method": collection_method,
- "status": status,
- "marker_ids": marker_ids,
- "provider_ids": provider_ids,
- "name": name,
- "order_key": order_key,
- "order_direction": order_direction,
- },
request_options=request_options,
)
try:
if 200 <= _response.status_code < 300:
_data = typing.cast(
- typing.List[ClientFacingLabTest],
+ GetUnmatchedResultTestResponse,
parse_obj_as(
- type_=typing.List[ClientFacingLabTest], # type: ignore
+ type_=GetUnmatchedResultTestResponse, # type: ignore
object_=_response.json(),
),
)
- return AsyncHttpResponse(response=_response, data=_data)
+ return HttpResponse(response=_response, data=_data)
if _response.status_code == 422:
raise UnprocessableEntityError(
headers=dict(_response.headers),
@@ -3339,77 +3386,78 @@ async def get(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- async def create(
+ def list_unmatched_results(
self,
*,
- name: str,
- method: LabTestCollectionMethod,
- description: str,
- marker_ids: typing.Optional[typing.Sequence[int]] = OMIT,
- provider_ids: typing.Optional[typing.Sequence[str]] = OMIT,
- fasting: typing.Optional[bool] = OMIT,
- lab_account_id: typing.Optional[str] = OMIT,
- lab_slug: typing.Optional[Labs] = OMIT,
+ limit: typing.Optional[int] = None,
+ next_cursor: typing.Optional[str] = None,
+ decision_code: typing.Optional[MatchDecisionCode] = None,
+ lab_slug: typing.Optional[str] = None,
+ status: typing.Optional[MatchReviewStatusFilter] = None,
+ created_at_start: typing.Optional[str] = None,
+ created_at_end: typing.Optional[str] = None,
+ search_input: typing.Optional[str] = None,
request_options: typing.Optional[RequestOptions] = None,
- ) -> AsyncHttpResponse[ClientFacingLabTest]:
+ ) -> HttpResponse[ListUnmatchedResultResponse]:
"""
Parameters
----------
- name : str
+ limit : typing.Optional[int]
- method : LabTestCollectionMethod
- ℹ️ This enum is non-exhaustive.
+ next_cursor : typing.Optional[str]
+ The cursor for fetching the next page, or `null` to fetch the first page.
- description : str
+ decision_code : typing.Optional[MatchDecisionCode]
+ Filter by match decision code.
- marker_ids : typing.Optional[typing.Sequence[int]]
+ lab_slug : typing.Optional[str]
+ Filter by lab slug (e.g. `labcorp`, `quest`).
- provider_ids : typing.Optional[typing.Sequence[str]]
+ status : typing.Optional[MatchReviewStatusFilter]
+ Filter by review status. `pending_customer_review` returns items awaiting your action; `pending_ops_review` returns items you have escalated for review.
- fasting : typing.Optional[bool]
+ created_at_start : typing.Optional[str]
+ Filter by result receipt date on or after this date (UTC, inclusive, YYYY-MM-DD).
- lab_account_id : typing.Optional[str]
+ created_at_end : typing.Optional[str]
+ Filter by result receipt date on or before this date (UTC, inclusive, YYYY-MM-DD).
- lab_slug : typing.Optional[Labs]
- ℹ️ This enum is non-exhaustive.
+ search_input : typing.Optional[str]
+ Search by patient first name, last name, or date of birth (e.g. `Alice`, `Smith`, or `1990-01-15`).
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- AsyncHttpResponse[ClientFacingLabTest]
+ HttpResponse[ListUnmatchedResultResponse]
Successful Response
"""
- _response = await self._client_wrapper.httpx_client.request(
- "v3/lab_tests",
- method="POST",
- json={
- "marker_ids": marker_ids,
- "provider_ids": provider_ids,
- "name": name,
- "method": method,
- "description": description,
- "fasting": fasting,
- "lab_account_id": lab_account_id,
+ _response = self._client_wrapper.httpx_client.request(
+ "v3/unmatched_result",
+ method="GET",
+ params={
+ "limit": limit,
+ "next_cursor": next_cursor,
+ "decision_code": decision_code,
"lab_slug": lab_slug,
- },
- headers={
- "content-type": "application/json",
+ "status": status,
+ "created_at_start": created_at_start,
+ "created_at_end": created_at_end,
+ "search_input": search_input,
},
request_options=request_options,
- omit=OMIT,
)
try:
if 200 <= _response.status_code < 300:
_data = typing.cast(
- ClientFacingLabTest,
+ ListUnmatchedResultResponse,
parse_obj_as(
- type_=ClientFacingLabTest, # type: ignore
+ type_=ListUnmatchedResultResponse, # type: ignore
object_=_response.json(),
),
)
- return AsyncHttpResponse(response=_response, data=_data)
+ return HttpResponse(response=_response, data=_data)
if _response.status_code == 422:
raise UnprocessableEntityError(
headers=dict(_response.headers),
@@ -3430,49 +3478,37 @@ async def create(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- async def get_by_id(
- self,
- lab_test_id: str,
- *,
- lab_account_id: typing.Optional[str] = None,
- request_options: typing.Optional[RequestOptions] = None,
- ) -> AsyncHttpResponse[ClientFacingLabTest]:
+ def get_unmatched_result(
+ self, raw_result_id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> HttpResponse[GetUnmatchedResultResponse]:
"""
- GET all the lab tests the team has access to.
-
Parameters
----------
- lab_test_id : str
-
- lab_account_id : typing.Optional[str]
- The lab account ID. This lab account is used to determine the availability of markers and lab tests.
+ raw_result_id : str
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- AsyncHttpResponse[ClientFacingLabTest]
+ HttpResponse[GetUnmatchedResultResponse]
Successful Response
"""
- _response = await self._client_wrapper.httpx_client.request(
- f"v3/lab_tests/{encode_path_param(lab_test_id)}",
+ _response = self._client_wrapper.httpx_client.request(
+ f"v3/unmatched_result/{encode_path_param(raw_result_id)}",
method="GET",
- params={
- "lab_account_id": lab_account_id,
- },
request_options=request_options,
)
try:
if 200 <= _response.status_code < 300:
_data = typing.cast(
- ClientFacingLabTest,
+ GetUnmatchedResultResponse,
parse_obj_as(
- type_=ClientFacingLabTest, # type: ignore
+ type_=GetUnmatchedResultResponse, # type: ignore
object_=_response.json(),
),
)
- return AsyncHttpResponse(response=_response, data=_data)
+ return HttpResponse(response=_response, data=_data)
if _response.status_code == 422:
raise UnprocessableEntityError(
headers=dict(_response.headers),
@@ -3493,37 +3529,41 @@ async def get_by_id(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- async def update_lab_test(
+ def accept_unmatched_result(
self,
- lab_test_id: str,
+ raw_result_id: str,
*,
- name: typing.Optional[str] = OMIT,
- active: typing.Optional[bool] = OMIT,
+ user_id: typing.Optional[str] = OMIT,
+ order_id: typing.Optional[str] = OMIT,
+ note: typing.Optional[str] = OMIT,
request_options: typing.Optional[RequestOptions] = None,
- ) -> AsyncHttpResponse[ClientFacingLabTest]:
+ ) -> HttpResponse[ClientFacingOrder]:
"""
Parameters
----------
- lab_test_id : str
+ raw_result_id : str
- name : typing.Optional[str]
+ user_id : typing.Optional[str]
- active : typing.Optional[bool]
+ order_id : typing.Optional[str]
+
+ note : typing.Optional[str]
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- AsyncHttpResponse[ClientFacingLabTest]
+ HttpResponse[ClientFacingOrder]
Successful Response
"""
- _response = await self._client_wrapper.httpx_client.request(
- f"v3/lab_tests/{encode_path_param(lab_test_id)}",
- method="PATCH",
+ _response = self._client_wrapper.httpx_client.request(
+ f"v3/unmatched_result/{encode_path_param(raw_result_id)}/accept",
+ method="POST",
json={
- "name": name,
- "active": active,
+ "user_id": user_id,
+ "order_id": order_id,
+ "note": note,
},
headers={
"content-type": "application/json",
@@ -3534,13 +3574,13 @@ async def update_lab_test(
try:
if 200 <= _response.status_code < 300:
_data = typing.cast(
- ClientFacingLabTest,
+ ClientFacingOrder,
parse_obj_as(
- type_=ClientFacingLabTest, # type: ignore
+ type_=ClientFacingOrder, # type: ignore
object_=_response.json(),
),
)
- return AsyncHttpResponse(response=_response, data=_data)
+ return HttpResponse(response=_response, data=_data)
if _response.status_code == 422:
raise UnprocessableEntityError(
headers=dict(_response.headers),
@@ -3561,73 +3601,55 @@ async def update_lab_test(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- async def get_markers(
+ def resolve_unmatched_result(
self,
+ raw_result_id: str,
*,
- lab_id: typing.Optional[typing.Union[int, typing.Sequence[int]]] = None,
- lab_slug: typing.Optional[str] = None,
- name: typing.Optional[str] = None,
- a_la_carte_enabled: typing.Optional[bool] = None,
- lab_account_id: typing.Optional[str] = None,
- page: typing.Optional[int] = None,
- size: typing.Optional[int] = None,
+ action: UnmatchedResultResolutionAction,
+ note: typing.Optional[str] = OMIT,
request_options: typing.Optional[RequestOptions] = None,
- ) -> AsyncHttpResponse[GetMarkersResponse]:
+ ) -> HttpResponse[UnmatchedResult]:
"""
- List active and orderable markers for a given Lab. Note that reflex markers are not included.
-
Parameters
----------
- lab_id : typing.Optional[typing.Union[int, typing.Sequence[int]]]
- The identifier Vital assigned to a lab partner.
+ raw_result_id : str
- lab_slug : typing.Optional[str]
- The slug of the lab for these markers. If both lab_id and lab_slug are provided, lab_slug will be used.
+ action : UnmatchedResultResolutionAction
+ ℹ️ This enum is non-exhaustive.
- name : typing.Optional[str]
- The name or test code of an individual biomarker or a panel.
-
- a_la_carte_enabled : typing.Optional[bool]
-
- lab_account_id : typing.Optional[str]
- The lab account ID. This lab account is used to determine the availability of markers and lab tests.
-
- page : typing.Optional[int]
-
- size : typing.Optional[int]
+ note : typing.Optional[str]
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- AsyncHttpResponse[GetMarkersResponse]
+ HttpResponse[UnmatchedResult]
Successful Response
"""
- _response = await self._client_wrapper.httpx_client.request(
- "v3/lab_tests/markers",
- method="GET",
- params={
- "lab_id": lab_id,
- "lab_slug": lab_slug,
- "name": name,
- "a_la_carte_enabled": a_la_carte_enabled,
- "lab_account_id": lab_account_id,
- "page": page,
- "size": size,
+ _response = self._client_wrapper.httpx_client.request(
+ f"v3/unmatched_result/{encode_path_param(raw_result_id)}/resolve",
+ method="POST",
+ json={
+ "action": action,
+ "note": note,
+ },
+ headers={
+ "content-type": "application/json",
},
request_options=request_options,
+ omit=OMIT,
)
try:
if 200 <= _response.status_code < 300:
_data = typing.cast(
- GetMarkersResponse,
+ UnmatchedResult,
parse_obj_as(
- type_=GetMarkersResponse, # type: ignore
+ type_=UnmatchedResult, # type: ignore
object_=_response.json(),
),
)
- return AsyncHttpResponse(response=_response, data=_data)
+ return HttpResponse(response=_response, data=_data)
if _response.status_code == 422:
raise UnprocessableEntityError(
headers=dict(_response.headers),
@@ -3648,48 +3670,27 @@ async def get_markers(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- async def get_markers_for_order_set(
- self,
- *,
- page: typing.Optional[int] = None,
- size: typing.Optional[int] = None,
- lab_test_ids: typing.Optional[typing.Sequence[str]] = OMIT,
- add_on: typing.Optional[AddOnOrder] = OMIT,
- lab_account_id: typing.Optional[str] = OMIT,
- request_options: typing.Optional[RequestOptions] = None,
- ) -> AsyncHttpResponse[GetMarkersResponse]:
+ def validate_icd_codes(
+ self, *, codes: typing.Sequence[str], request_options: typing.Optional[RequestOptions] = None
+ ) -> HttpResponse[ValidateIcdCodesResponse]:
"""
Parameters
----------
- page : typing.Optional[int]
-
- size : typing.Optional[int]
-
- lab_test_ids : typing.Optional[typing.Sequence[str]]
-
- add_on : typing.Optional[AddOnOrder]
-
- lab_account_id : typing.Optional[str]
+ codes : typing.Sequence[str]
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- AsyncHttpResponse[GetMarkersResponse]
+ HttpResponse[ValidateIcdCodesResponse]
Successful Response
"""
- _response = await self._client_wrapper.httpx_client.request(
- "v3/lab_tests/list_order_set_markers",
+ _response = self._client_wrapper.httpx_client.request(
+ "v3/insurance/validate_icd_codes",
method="POST",
- params={
- "page": page,
- "size": size,
- },
json={
- "lab_test_ids": lab_test_ids,
- "add_on": add_on,
- "lab_account_id": lab_account_id,
+ "codes": codes,
},
headers={
"content-type": "application/json",
@@ -3700,13 +3701,13 @@ async def get_markers_for_order_set(
try:
if 200 <= _response.status_code < 300:
_data = typing.cast(
- GetMarkersResponse,
+ ValidateIcdCodesResponse,
parse_obj_as(
- type_=GetMarkersResponse, # type: ignore
+ type_=ValidateIcdCodesResponse, # type: ignore
object_=_response.json(),
),
)
- return AsyncHttpResponse(response=_response, data=_data)
+ return HttpResponse(response=_response, data=_data)
if _response.status_code == 422:
raise UnprocessableEntityError(
headers=dict(_response.headers),
@@ -3727,53 +3728,85 @@ async def get_markers_for_order_set(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- async def get_markers_for_lab_test(
+
+class AsyncRawLabTestsClient:
+ def __init__(self, *, client_wrapper: AsyncClientWrapper):
+ self._client_wrapper = client_wrapper
+
+ async def get(
self,
- lab_test_id: str,
*,
- lab_account_id: typing.Optional[str] = None,
- page: typing.Optional[int] = None,
- size: typing.Optional[int] = None,
+ generation_method: typing.Optional[LabTestGenerationMethodFilter] = None,
+ lab_slug: typing.Optional[str] = None,
+ collection_method: typing.Optional[LabTestCollectionMethod] = None,
+ status: typing.Optional[LabTestStatus] = None,
+ marker_ids: typing.Optional[typing.Union[int, typing.Sequence[int]]] = None,
+ provider_ids: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None,
+ name: typing.Optional[str] = None,
+ order_key: typing.Optional[GetLabTestsRequestOrderKey] = None,
+ order_direction: typing.Optional[GetLabTestsRequestOrderDirection] = None,
request_options: typing.Optional[RequestOptions] = None,
- ) -> AsyncHttpResponse[GetMarkersResponse]:
+ ) -> AsyncHttpResponse[typing.List[ClientFacingLabTest]]:
"""
- List all markers for a given Lab Test, as well as any associated reflex markers.
+ GET all the lab tests the team has access to.
Parameters
----------
- lab_test_id : str
+ generation_method : typing.Optional[LabTestGenerationMethodFilter]
+ Filter on whether auto-generated lab tests created by Vital, manually created lab tests, or all lab tests should be returned.
- lab_account_id : typing.Optional[str]
- The lab account ID. This lab account is used to determine the availability of markers and lab tests.
+ lab_slug : typing.Optional[str]
+ Filter by the slug of the lab for these lab tests.
- page : typing.Optional[int]
+ collection_method : typing.Optional[LabTestCollectionMethod]
+ Filter by the collection method for these lab tests.
- size : typing.Optional[int]
+ status : typing.Optional[LabTestStatus]
+ Filter by the status of these lab tests.
+
+ marker_ids : typing.Optional[typing.Union[int, typing.Sequence[int]]]
+ Filter to only include lab tests containing these marker IDs.
+
+ provider_ids : typing.Optional[typing.Union[str, typing.Sequence[str]]]
+ Filter to only include lab tests containing these provider IDs.
+
+ name : typing.Optional[str]
+ Filter by the name of the lab test (a case-insensitive substring search).
+
+ order_key : typing.Optional[GetLabTestsRequestOrderKey]
+
+ order_direction : typing.Optional[GetLabTestsRequestOrderDirection]
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- AsyncHttpResponse[GetMarkersResponse]
+ AsyncHttpResponse[typing.List[ClientFacingLabTest]]
Successful Response
"""
_response = await self._client_wrapper.httpx_client.request(
- f"v3/lab_tests/{encode_path_param(lab_test_id)}/markers",
+ "v3/lab_tests",
method="GET",
params={
- "lab_account_id": lab_account_id,
- "page": page,
- "size": size,
+ "generation_method": generation_method,
+ "lab_slug": lab_slug,
+ "collection_method": collection_method,
+ "status": status,
+ "marker_ids": marker_ids,
+ "provider_ids": provider_ids,
+ "name": name,
+ "order_key": order_key,
+ "order_direction": order_direction,
},
request_options=request_options,
)
try:
if 200 <= _response.status_code < 300:
_data = typing.cast(
- GetMarkersResponse,
+ typing.List[ClientFacingLabTest],
parse_obj_as(
- type_=GetMarkersResponse, # type: ignore
+ type_=typing.List[ClientFacingLabTest], # type: ignore
object_=_response.json(),
),
)
@@ -3798,48 +3831,73 @@ async def get_markers_for_lab_test(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- async def get_markers_by_lab_and_provider_id(
+ async def create(
self,
- provider_id: str,
- lab_id: int,
*,
- lab_account_id: typing.Optional[str] = None,
+ name: str,
+ method: LabTestCollectionMethod,
+ description: str,
+ marker_ids: typing.Optional[typing.Sequence[int]] = OMIT,
+ provider_ids: typing.Optional[typing.Sequence[str]] = OMIT,
+ fasting: typing.Optional[bool] = OMIT,
+ lab_account_id: typing.Optional[str] = OMIT,
+ lab_slug: typing.Optional[Labs] = OMIT,
request_options: typing.Optional[RequestOptions] = None,
- ) -> AsyncHttpResponse[ClientFacingMarker]:
+ ) -> AsyncHttpResponse[ClientFacingLabTest]:
"""
- GET a specific marker for the given lab and provider_id
-
Parameters
----------
- provider_id : str
+ name : str
- lab_id : int
+ method : LabTestCollectionMethod
+ ℹ️ This enum is non-exhaustive.
+
+ description : str
+
+ marker_ids : typing.Optional[typing.Sequence[int]]
+
+ provider_ids : typing.Optional[typing.Sequence[str]]
+
+ fasting : typing.Optional[bool]
lab_account_id : typing.Optional[str]
- The lab account ID. This lab account is used to determine the availability of markers and lab tests.
+
+ lab_slug : typing.Optional[Labs]
+ ℹ️ This enum is non-exhaustive.
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- AsyncHttpResponse[ClientFacingMarker]
+ AsyncHttpResponse[ClientFacingLabTest]
Successful Response
"""
_response = await self._client_wrapper.httpx_client.request(
- f"v3/lab_tests/{encode_path_param(lab_id)}/markers/{encode_path_param(provider_id)}",
- method="GET",
- params={
+ "v3/lab_tests",
+ method="POST",
+ json={
+ "marker_ids": marker_ids,
+ "provider_ids": provider_ids,
+ "name": name,
+ "method": method,
+ "description": description,
+ "fasting": fasting,
"lab_account_id": lab_account_id,
+ "lab_slug": lab_slug,
+ },
+ headers={
+ "content-type": "application/json",
},
request_options=request_options,
+ omit=OMIT,
)
try:
if 200 <= _response.status_code < 300:
_data = typing.cast(
- ClientFacingMarker,
+ ClientFacingLabTest,
parse_obj_as(
- type_=ClientFacingMarker, # type: ignore
+ type_=ClientFacingLabTest, # type: ignore
object_=_response.json(),
),
)
@@ -3864,37 +3922,60 @@ async def get_markers_by_lab_and_provider_id(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- async def get_labs(
- self, *, request_options: typing.Optional[RequestOptions] = None
- ) -> AsyncHttpResponse[typing.List[ClientFacingLab]]:
+ async def get_by_id(
+ self,
+ lab_test_id: str,
+ *,
+ lab_account_id: typing.Optional[str] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> AsyncHttpResponse[ClientFacingLabTest]:
"""
- GET all the labs.
+ GET all the lab tests the team has access to.
Parameters
----------
+ lab_test_id : str
+
+ lab_account_id : typing.Optional[str]
+ The lab account ID. This lab account is used to determine the availability of markers and lab tests.
+
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- AsyncHttpResponse[typing.List[ClientFacingLab]]
+ AsyncHttpResponse[ClientFacingLabTest]
Successful Response
"""
_response = await self._client_wrapper.httpx_client.request(
- "v3/lab_tests/labs",
+ f"v3/lab_tests/{encode_path_param(lab_test_id)}",
method="GET",
+ params={
+ "lab_account_id": lab_account_id,
+ },
request_options=request_options,
)
try:
if 200 <= _response.status_code < 300:
_data = typing.cast(
- typing.List[ClientFacingLab],
+ ClientFacingLabTest,
parse_obj_as(
- type_=typing.List[ClientFacingLab], # type: ignore
+ type_=ClientFacingLabTest, # type: ignore
object_=_response.json(),
),
)
return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 422:
+ raise UnprocessableEntityError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ HttpValidationError,
+ parse_obj_as(
+ type_=HttpValidationError, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
_response_json = _response.json()
except JSONDecodeError:
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
@@ -3904,88 +3985,50 @@ async def get_labs(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- async def get_paginated(
+ async def update_lab_test(
self,
+ lab_test_id: str,
*,
- lab_test_limit: typing.Optional[int] = None,
- next_cursor: typing.Optional[str] = None,
- generation_method: typing.Optional[LabTestGenerationMethodFilter] = None,
- lab_slug: typing.Optional[str] = None,
- collection_method: typing.Optional[LabTestCollectionMethod] = None,
- status: typing.Optional[LabTestStatus] = None,
- marker_ids: typing.Optional[typing.Union[int, typing.Sequence[int]]] = None,
- provider_ids: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None,
- name: typing.Optional[str] = None,
- order_key: typing.Optional[GetPaginatedLabTestsRequestOrderKey] = None,
- order_direction: typing.Optional[GetPaginatedLabTestsRequestOrderDirection] = None,
+ name: typing.Optional[str] = OMIT,
+ active: typing.Optional[bool] = OMIT,
request_options: typing.Optional[RequestOptions] = None,
- ) -> AsyncHttpResponse[LabTestResourcesResponse]:
+ ) -> AsyncHttpResponse[ClientFacingLabTest]:
"""
- GET lab tests the team has access to as a paginated list.
-
Parameters
----------
- lab_test_limit : typing.Optional[int]
-
- next_cursor : typing.Optional[str]
-
- generation_method : typing.Optional[LabTestGenerationMethodFilter]
- Filter on whether auto-generated lab tests created by Vital, manually created lab tests, or all lab tests should be returned.
-
- lab_slug : typing.Optional[str]
- Filter by the slug of the lab for these lab tests.
-
- collection_method : typing.Optional[LabTestCollectionMethod]
- Filter by the collection method for these lab tests.
-
- status : typing.Optional[LabTestStatus]
- Filter by the status of these lab tests.
-
- marker_ids : typing.Optional[typing.Union[int, typing.Sequence[int]]]
- Filter to only include lab tests containing these marker IDs.
-
- provider_ids : typing.Optional[typing.Union[str, typing.Sequence[str]]]
- Filter to only include lab tests containing these provider IDs.
+ lab_test_id : str
name : typing.Optional[str]
- Filter by the name of the lab test (a case-insensitive substring search).
-
- order_key : typing.Optional[GetPaginatedLabTestsRequestOrderKey]
- order_direction : typing.Optional[GetPaginatedLabTestsRequestOrderDirection]
+ active : typing.Optional[bool]
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- AsyncHttpResponse[LabTestResourcesResponse]
+ AsyncHttpResponse[ClientFacingLabTest]
Successful Response
"""
_response = await self._client_wrapper.httpx_client.request(
- "v3/lab_test",
- method="GET",
- params={
- "lab_test_limit": lab_test_limit,
- "next_cursor": next_cursor,
- "generation_method": generation_method,
- "lab_slug": lab_slug,
- "collection_method": collection_method,
- "status": status,
- "marker_ids": marker_ids,
- "provider_ids": provider_ids,
+ f"v3/lab_tests/{encode_path_param(lab_test_id)}",
+ method="PATCH",
+ json={
"name": name,
- "order_key": order_key,
- "order_direction": order_direction,
+ "active": active,
+ },
+ headers={
+ "content-type": "application/json",
},
request_options=request_options,
+ omit=OMIT,
)
try:
if 200 <= _response.status_code < 300:
_data = typing.cast(
- LabTestResourcesResponse,
+ ClientFacingLabTest,
parse_obj_as(
- type_=LabTestResourcesResponse, # type: ignore
+ type_=ClientFacingLabTest, # type: ignore
object_=_response.json(),
),
)
@@ -4010,148 +4053,39 @@ async def get_paginated(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- @contextlib.asynccontextmanager
- async def get_lab_test_collection_instruction_pdf(
- self, lab_test_id: str, *, request_options: typing.Optional[RequestOptions] = None
- ) -> typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[bytes]]]:
- """
- Parameters
- ----------
- lab_test_id : str
-
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration. You can pass in configuration such as `chunk_size`, and more to customize the request and response.
-
- Returns
- -------
- typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[bytes]]]
- PDF with collection instructions
- """
- async with self._client_wrapper.httpx_client.stream(
- f"v3/lab_test/{encode_path_param(lab_test_id)}/collection_instruction_pdf",
- method="GET",
- request_options=request_options,
- ) as _response:
-
- async def _stream() -> AsyncHttpResponse[typing.AsyncIterator[bytes]]:
- try:
- if 200 <= _response.status_code < 300:
- _chunk_size = request_options.get("chunk_size", None) if request_options is not None else None
- return AsyncHttpResponse(
- response=_response,
- data=(_chunk async for _chunk in _response.aiter_bytes(chunk_size=_chunk_size)),
- )
- await _response.aread()
- if _response.status_code == 422:
- raise UnprocessableEntityError(
- headers=dict(_response.headers),
- body=typing.cast(
- HttpValidationError,
- parse_obj_as(
- type_=HttpValidationError, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- _response_json = _response.json()
- except JSONDecodeError:
- raise ApiError(
- status_code=_response.status_code, headers=dict(_response.headers), body=_response.text
- )
- except ValidationError as e:
- raise ParsingError(
- status_code=_response.status_code,
- headers=dict(_response.headers),
- body=_response.json(),
- cause=e,
- )
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
-
- yield await _stream()
-
- async def get_orders(
+ async def get_markers(
self,
*,
- search_input: typing.Optional[str] = None,
- start_date: typing.Optional[dt.datetime] = None,
- end_date: typing.Optional[dt.datetime] = None,
- updated_start_date: typing.Optional[dt.datetime] = None,
- updated_end_date: typing.Optional[dt.datetime] = None,
- status: typing.Optional[typing.Union[OrderLowLevelStatus, typing.Sequence[OrderLowLevelStatus]]] = None,
- order_key: typing.Optional[GetOrdersLabTestsRequestOrderKey] = None,
- order_direction: typing.Optional[GetOrdersLabTestsRequestOrderDirection] = None,
- order_type: typing.Optional[
- typing.Union[LabTestCollectionMethod, typing.Sequence[LabTestCollectionMethod]]
- ] = None,
- is_critical: typing.Optional[bool] = None,
- interpretation: typing.Optional[Interpretation] = None,
- order_activation_types: typing.Optional[
- typing.Union[OrderActivationType, typing.Sequence[OrderActivationType]]
- ] = None,
- user_id: typing.Optional[str] = None,
- patient_name: typing.Optional[str] = None,
- shipping_recipient_name: typing.Optional[str] = None,
- order_ids: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None,
- order_transaction_id: typing.Optional[str] = None,
+ lab_id: typing.Optional[typing.Union[int, typing.Sequence[int]]] = None,
+ lab_slug: typing.Optional[str] = None,
+ name: typing.Optional[str] = None,
+ a_la_carte_enabled: typing.Optional[bool] = None,
+ include_pricing: typing.Optional[bool] = None,
+ lab_account_id: typing.Optional[str] = None,
page: typing.Optional[int] = None,
size: typing.Optional[int] = None,
request_options: typing.Optional[RequestOptions] = None,
- ) -> AsyncHttpResponse[GetOrdersResponse]:
+ ) -> AsyncHttpResponse[GetMarkersResponse]:
"""
- GET many orders with filters.
+ List active and orderable markers for a given Lab. Note that reflex markers are not included.
Parameters
----------
- search_input : typing.Optional[str]
- Search by order id, user id, patient name, shipping dob, or shipping recipient name.
-
- start_date : typing.Optional[dt.datetime]
- Date from in YYYY-MM-DD or ISO formatted date time. If a date is provided without a time, the time will be set to 00:00:00
-
- end_date : typing.Optional[dt.datetime]
- Date to YYYY-MM-DD or ISO formatted date time. If a date is provided without a time, the time will be set to 23:59:59
-
- updated_start_date : typing.Optional[dt.datetime]
- Date from in YYYY-MM-DD or ISO formatted date time. If a date is provided without a time, the time will be set to 00:00:00
-
- updated_end_date : typing.Optional[dt.datetime]
- Date to YYYY-MM-DD or ISO formatted date time. If a date is provided without a time, the time will be set to 00:00:00
-
- status : typing.Optional[typing.Union[OrderLowLevelStatus, typing.Sequence[OrderLowLevelStatus]]]
- Filter by low level status.
-
- order_key : typing.Optional[GetOrdersLabTestsRequestOrderKey]
- Order key to sort by.
-
- order_direction : typing.Optional[GetOrdersLabTestsRequestOrderDirection]
- Order direction to sort by.
-
- order_type : typing.Optional[typing.Union[LabTestCollectionMethod, typing.Sequence[LabTestCollectionMethod]]]
- Filter by method used to perform the lab test.
-
- is_critical : typing.Optional[bool]
- Filter by critical order status.
-
- interpretation : typing.Optional[Interpretation]
- Filter by result interpretation of the lab test.
-
- order_activation_types : typing.Optional[typing.Union[OrderActivationType, typing.Sequence[OrderActivationType]]]
- Filter by activation type.
+ lab_id : typing.Optional[typing.Union[int, typing.Sequence[int]]]
+ The identifier Vital assigned to a lab partner.
- user_id : typing.Optional[str]
- Filter by user ID.
+ lab_slug : typing.Optional[str]
+ The slug of the lab for these markers. If both lab_id and lab_slug are provided, lab_slug will be used.
- patient_name : typing.Optional[str]
- Filter by patient name.
+ name : typing.Optional[str]
+ The name or test code of an individual biomarker or a panel.
- shipping_recipient_name : typing.Optional[str]
- Filter by shipping recipient name.
+ a_la_carte_enabled : typing.Optional[bool]
- order_ids : typing.Optional[typing.Union[str, typing.Sequence[str]]]
- Filter by order ids.
+ include_pricing : typing.Optional[bool]
- order_transaction_id : typing.Optional[str]
- Filter by order transaction ID
+ lab_account_id : typing.Optional[str]
+ The lab account ID. This lab account is used to determine the availability of markers and lab tests.
page : typing.Optional[int]
@@ -4162,32 +4096,19 @@ async def get_orders(
Returns
-------
- AsyncHttpResponse[GetOrdersResponse]
+ AsyncHttpResponse[GetMarkersResponse]
Successful Response
"""
_response = await self._client_wrapper.httpx_client.request(
- "v3/orders",
+ "v3/lab_tests/markers",
method="GET",
params={
- "search_input": search_input,
- "start_date": serialize_datetime(start_date) if start_date is not None else None,
- "end_date": serialize_datetime(end_date) if end_date is not None else None,
- "updated_start_date": serialize_datetime(updated_start_date)
- if updated_start_date is not None
- else None,
- "updated_end_date": serialize_datetime(updated_end_date) if updated_end_date is not None else None,
- "status": status,
- "order_key": order_key,
- "order_direction": order_direction,
- "order_type": order_type,
- "is_critical": is_critical,
- "interpretation": interpretation,
- "order_activation_types": order_activation_types,
- "user_id": user_id,
- "patient_name": patient_name,
- "shipping_recipient_name": shipping_recipient_name,
- "order_ids": order_ids,
- "order_transaction_id": order_transaction_id,
+ "lab_id": lab_id,
+ "lab_slug": lab_slug,
+ "name": name,
+ "a_la_carte_enabled": a_la_carte_enabled,
+ "include_pricing": include_pricing,
+ "lab_account_id": lab_account_id,
"page": page,
"size": size,
},
@@ -4196,9 +4117,9 @@ async def get_orders(
try:
if 200 <= _response.status_code < 300:
_data = typing.cast(
- GetOrdersResponse,
+ GetMarkersResponse,
parse_obj_as(
- type_=GetOrdersResponse, # type: ignore
+ type_=GetMarkersResponse, # type: ignore
object_=_response.json(),
),
)
@@ -4223,65 +4144,48 @@ async def get_orders(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- async def get_phlebotomy_appointment_availability(
+ async def get_markers_for_order_set(
self,
*,
- first_line: str,
- city: str,
- state: str,
- zip_code: str,
- start_date: typing.Optional[str] = None,
- second_line: typing.Optional[str] = OMIT,
- unit: typing.Optional[str] = OMIT,
- access_notes: typing.Optional[str] = OMIT,
+ page: typing.Optional[int] = None,
+ size: typing.Optional[int] = None,
+ lab_test_ids: typing.Optional[typing.Sequence[str]] = OMIT,
+ add_on: typing.Optional[AddOnOrder] = OMIT,
+ lab_account_id: typing.Optional[str] = OMIT,
request_options: typing.Optional[RequestOptions] = None,
- ) -> AsyncHttpResponse[AppointmentAvailabilitySlots]:
+ ) -> AsyncHttpResponse[GetMarkersResponse]:
"""
- Return the available time slots to book an appointment with a phlebotomist
- for the given address and order.
-
Parameters
----------
- first_line : str
-
- city : str
-
- state : str
-
- zip_code : str
+ page : typing.Optional[int]
- start_date : typing.Optional[str]
- Start date for appointment availability
+ size : typing.Optional[int]
- second_line : typing.Optional[str]
+ lab_test_ids : typing.Optional[typing.Sequence[str]]
- unit : typing.Optional[str]
- Deprecated. Use `second_line` instead to provide the unit number. Subject to removal after 20 Nov 2023.
+ add_on : typing.Optional[AddOnOrder]
- access_notes : typing.Optional[str]
+ lab_account_id : typing.Optional[str]
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- AsyncHttpResponse[AppointmentAvailabilitySlots]
+ AsyncHttpResponse[GetMarkersResponse]
Successful Response
"""
_response = await self._client_wrapper.httpx_client.request(
- "v3/order/phlebotomy/appointment/availability",
+ "v3/lab_tests/list_order_set_markers",
method="POST",
params={
- "start_date": start_date,
+ "page": page,
+ "size": size,
},
json={
- "first_line": first_line,
- "second_line": second_line,
- "city": city,
- "state": state,
- "zip_code": zip_code,
- "unit": unit,
- "access_notes": access_notes,
+ "lab_test_ids": lab_test_ids,
+ "add_on": add_on,
+ "lab_account_id": lab_account_id,
},
headers={
"content-type": "application/json",
@@ -4292,9 +4196,9 @@ async def get_phlebotomy_appointment_availability(
try:
if 200 <= _response.status_code < 300:
_data = typing.cast(
- AppointmentAvailabilitySlots,
+ GetMarkersResponse,
parse_obj_as(
- type_=AppointmentAvailabilitySlots, # type: ignore
+ type_=GetMarkersResponse, # type: ignore
object_=_response.json(),
),
)
@@ -4319,68 +4223,53 @@ async def get_phlebotomy_appointment_availability(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- async def book_phlebotomy_appointment(
+ async def get_markers_for_lab_test(
self,
- order_id: str,
+ lab_test_id: str,
*,
- booking_key: str,
- appointment_notes: typing.Optional[str] = OMIT,
- async_confirmation: typing.Optional[bool] = OMIT,
- sync_confirmation_timeout_millisecond: typing.Optional[int] = OMIT,
- async_confirmation_timeout_millisecond: typing.Optional[int] = OMIT,
+ lab_account_id: typing.Optional[str] = None,
+ page: typing.Optional[int] = None,
+ size: typing.Optional[int] = None,
request_options: typing.Optional[RequestOptions] = None,
- ) -> AsyncHttpResponse[ClientFacingAppointment]:
+ ) -> AsyncHttpResponse[GetMarkersResponse]:
"""
- Book an at-home phlebotomy appointment.
+ List all markers for a given Lab Test, as well as any associated reflex markers.
Parameters
----------
- order_id : str
- Your Order ID.
-
- booking_key : str
-
- appointment_notes : typing.Optional[str]
+ lab_test_id : str
- async_confirmation : typing.Optional[bool]
- If true, the endpoint attempts to confirm the booking within the `sync_confirmation_timeout_millisecond` window. If confirmation is not received in time, a pending appointment is returned and booking continues asynchronously. If false (default), the endpoint waits for confirmation or returns a 500 error on failure.
+ lab_account_id : typing.Optional[str]
+ The lab account ID. This lab account is used to determine the availability of markers and lab tests.
- sync_confirmation_timeout_millisecond : typing.Optional[int]
- Maximum time (in milliseconds) to wait for booking confirmation before returning a pending appointment. Only applies when `async_confirmation` is true. Defaults to 2500ms. Range: 1000-10000ms.
+ page : typing.Optional[int]
- async_confirmation_timeout_millisecond : typing.Optional[int]
- Maximum time (in milliseconds) to attempt asynchronous booking before cancelling the pending appointment. Only applies when `async_confirmation` is true. Defaults to 15 minutes. Range: 60000-172800000ms.
+ size : typing.Optional[int]
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- AsyncHttpResponse[ClientFacingAppointment]
+ AsyncHttpResponse[GetMarkersResponse]
Successful Response
"""
_response = await self._client_wrapper.httpx_client.request(
- f"v3/order/{encode_path_param(order_id)}/phlebotomy/appointment/book",
- method="POST",
- json={
- "booking_key": booking_key,
- "appointment_notes": appointment_notes,
- "async_confirmation": async_confirmation,
- "sync_confirmation_timeout_millisecond": sync_confirmation_timeout_millisecond,
- "async_confirmation_timeout_millisecond": async_confirmation_timeout_millisecond,
- },
- headers={
- "content-type": "application/json",
+ f"v3/lab_tests/{encode_path_param(lab_test_id)}/markers",
+ method="GET",
+ params={
+ "lab_account_id": lab_account_id,
+ "page": page,
+ "size": size,
},
request_options=request_options,
- omit=OMIT,
)
try:
if 200 <= _response.status_code < 300:
_data = typing.cast(
- ClientFacingAppointment,
+ GetMarkersResponse,
parse_obj_as(
- type_=ClientFacingAppointment, # type: ignore
+ type_=GetMarkersResponse, # type: ignore
object_=_response.json(),
),
)
@@ -4405,59 +4294,48 @@ async def book_phlebotomy_appointment(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- async def request_phlebotomy_appointment(
+ async def get_markers_by_lab_and_provider_id(
self,
- order_id: str,
+ provider_id: str,
+ lab_id: int,
*,
- address: UsAddress,
- provider: AppointmentProvider,
- appointment_notes: typing.Optional[str] = OMIT,
+ lab_account_id: typing.Optional[str] = None,
request_options: typing.Optional[RequestOptions] = None,
- ) -> AsyncHttpResponse[ClientFacingAppointment]:
+ ) -> AsyncHttpResponse[ClientFacingMarker]:
"""
- Request an at-home phlebotomy appointment.
+ GET a specific marker for the given lab and provider_id
Parameters
----------
- order_id : str
- Your Order ID.
-
- address : UsAddress
- At-home phlebotomy appointment address.
+ provider_id : str
- provider : AppointmentProvider
- ℹ️ This enum is non-exhaustive.
+ lab_id : int
- appointment_notes : typing.Optional[str]
+ lab_account_id : typing.Optional[str]
+ The lab account ID. This lab account is used to determine the availability of markers and lab tests.
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- AsyncHttpResponse[ClientFacingAppointment]
+ AsyncHttpResponse[ClientFacingMarker]
Successful Response
"""
_response = await self._client_wrapper.httpx_client.request(
- f"v3/order/{encode_path_param(order_id)}/phlebotomy/appointment/request",
- method="POST",
- json={
- "address": address,
- "provider": provider,
- "appointment_notes": appointment_notes,
- },
- headers={
- "content-type": "application/json",
+ f"v3/lab_tests/{encode_path_param(lab_id)}/markers/{encode_path_param(provider_id)}",
+ method="GET",
+ params={
+ "lab_account_id": lab_account_id,
},
request_options=request_options,
- omit=OMIT,
)
try:
if 200 <= _response.status_code < 300:
_data = typing.cast(
- ClientFacingAppointment,
+ ClientFacingMarker,
parse_obj_as(
- type_=ClientFacingAppointment, # type: ignore
+ type_=ClientFacingMarker, # type: ignore
object_=_response.json(),
),
)
@@ -4482,68 +4360,37 @@ async def request_phlebotomy_appointment(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- async def reschedule_phlebotomy_appointment(
- self,
- order_id: str,
- *,
- booking_key: str,
- appointment_notes: typing.Optional[str] = OMIT,
- request_options: typing.Optional[RequestOptions] = None,
- ) -> AsyncHttpResponse[ClientFacingAppointment]:
+ async def get_labs(
+ self, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> AsyncHttpResponse[typing.List[ClientFacingLab]]:
"""
- Reschedule a previously booked at-home phlebotomy appointment.
+ GET all the labs.
Parameters
----------
- order_id : str
- Your Order ID.
-
- booking_key : str
-
- appointment_notes : typing.Optional[str]
-
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- AsyncHttpResponse[ClientFacingAppointment]
+ AsyncHttpResponse[typing.List[ClientFacingLab]]
Successful Response
"""
_response = await self._client_wrapper.httpx_client.request(
- f"v3/order/{encode_path_param(order_id)}/phlebotomy/appointment/reschedule",
- method="PATCH",
- json={
- "booking_key": booking_key,
- "appointment_notes": appointment_notes,
- },
- headers={
- "content-type": "application/json",
- },
+ "v3/lab_tests/labs",
+ method="GET",
request_options=request_options,
- omit=OMIT,
)
try:
if 200 <= _response.status_code < 300:
_data = typing.cast(
- ClientFacingAppointment,
+ typing.List[ClientFacingLab],
parse_obj_as(
- type_=ClientFacingAppointment, # type: ignore
+ type_=typing.List[ClientFacingLab], # type: ignore
object_=_response.json(),
),
)
return AsyncHttpResponse(response=_response, data=_data)
- if _response.status_code == 422:
- raise UnprocessableEntityError(
- headers=dict(_response.headers),
- body=typing.cast(
- HttpValidationError,
- parse_obj_as(
- type_=HttpValidationError, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
_response_json = _response.json()
except JSONDecodeError:
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
@@ -4553,57 +4400,1381 @@ async def reschedule_phlebotomy_appointment(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- async def cancel_phlebotomy_appointment(
+ async def get_paginated(
self,
- order_id: str,
*,
- cancellation_reason_id: str,
- notes: typing.Optional[str] = OMIT,
+ lab_test_limit: typing.Optional[int] = None,
+ next_cursor: typing.Optional[str] = None,
+ include_pricing: typing.Optional[bool] = None,
+ lab_account_id: typing.Optional[str] = None,
+ generation_method: typing.Optional[LabTestGenerationMethodFilter] = None,
+ lab_slug: typing.Optional[str] = None,
+ collection_method: typing.Optional[LabTestCollectionMethod] = None,
+ status: typing.Optional[LabTestStatus] = None,
+ marker_ids: typing.Optional[typing.Union[int, typing.Sequence[int]]] = None,
+ provider_ids: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None,
+ name: typing.Optional[str] = None,
+ order_key: typing.Optional[GetPaginatedLabTestsRequestOrderKey] = None,
+ order_direction: typing.Optional[GetPaginatedLabTestsRequestOrderDirection] = None,
request_options: typing.Optional[RequestOptions] = None,
- ) -> AsyncHttpResponse[ClientFacingAppointment]:
+ ) -> AsyncHttpResponse[LabTestResourcesResponse]:
"""
- Cancel a previously booked at-home phlebotomy appointment.
+ GET lab tests the team has access to as a paginated list.
Parameters
----------
- order_id : str
- Your Order ID.
+ lab_test_limit : typing.Optional[int]
- cancellation_reason_id : str
+ next_cursor : typing.Optional[str]
+ The cursor for fetching the next page, or `null` to fetch the first page.
- notes : typing.Optional[str]
+ include_pricing : typing.Optional[bool]
+
+ lab_account_id : typing.Optional[str]
+ The lab account ID. This lab account is used to determine the availability of markers and lab tests.
+
+ generation_method : typing.Optional[LabTestGenerationMethodFilter]
+ Filter on whether auto-generated lab tests created by Vital, manually created lab tests, or all lab tests should be returned.
+
+ lab_slug : typing.Optional[str]
+ Filter by the slug of the lab for these lab tests.
+
+ collection_method : typing.Optional[LabTestCollectionMethod]
+ Filter by the collection method for these lab tests.
+
+ status : typing.Optional[LabTestStatus]
+ Filter by the status of these lab tests.
+
+ marker_ids : typing.Optional[typing.Union[int, typing.Sequence[int]]]
+ Filter to only include lab tests containing these marker IDs.
+
+ provider_ids : typing.Optional[typing.Union[str, typing.Sequence[str]]]
+ Filter to only include lab tests containing these provider IDs.
+
+ name : typing.Optional[str]
+ Filter by the name of the lab test (a case-insensitive substring search).
+
+ order_key : typing.Optional[GetPaginatedLabTestsRequestOrderKey]
+
+ order_direction : typing.Optional[GetPaginatedLabTestsRequestOrderDirection]
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- AsyncHttpResponse[ClientFacingAppointment]
+ AsyncHttpResponse[LabTestResourcesResponse]
Successful Response
"""
_response = await self._client_wrapper.httpx_client.request(
- f"v3/order/{encode_path_param(order_id)}/phlebotomy/appointment/cancel",
- method="PATCH",
- json={
- "cancellation_reason_id": cancellation_reason_id,
- "notes": notes,
- },
+ "v3/lab_test",
+ method="GET",
+ params={
+ "lab_test_limit": lab_test_limit,
+ "next_cursor": next_cursor,
+ "include_pricing": include_pricing,
+ "lab_account_id": lab_account_id,
+ "generation_method": generation_method,
+ "lab_slug": lab_slug,
+ "collection_method": collection_method,
+ "status": status,
+ "marker_ids": marker_ids,
+ "provider_ids": provider_ids,
+ "name": name,
+ "order_key": order_key,
+ "order_direction": order_direction,
+ },
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ LabTestResourcesResponse,
+ parse_obj_as(
+ type_=LabTestResourcesResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 422:
+ raise UnprocessableEntityError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ HttpValidationError,
+ parse_obj_as(
+ type_=HttpValidationError, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ @contextlib.asynccontextmanager
+ async def get_lab_test_collection_instruction_pdf(
+ self, lab_test_id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[bytes]]]:
+ """
+ Parameters
+ ----------
+ lab_test_id : str
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration. You can pass in configuration such as `chunk_size`, and more to customize the request and response.
+
+ Returns
+ -------
+ typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[bytes]]]
+ PDF with collection instructions
+ """
+ async with self._client_wrapper.httpx_client.stream(
+ f"v3/lab_test/{encode_path_param(lab_test_id)}/collection_instruction_pdf",
+ method="GET",
+ request_options=request_options,
+ ) as _response:
+
+ async def _stream() -> AsyncHttpResponse[typing.AsyncIterator[bytes]]:
+ try:
+ if 200 <= _response.status_code < 300:
+ _chunk_size = request_options.get("chunk_size", None) if request_options is not None else None
+ return AsyncHttpResponse(
+ response=_response,
+ data=(_chunk async for _chunk in _response.aiter_bytes(chunk_size=_chunk_size)),
+ )
+ await _response.aread()
+ if _response.status_code == 422:
+ raise UnprocessableEntityError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ HttpValidationError,
+ parse_obj_as(
+ type_=HttpValidationError, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.text
+ )
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code,
+ headers=dict(_response.headers),
+ body=_response.json(),
+ cause=e,
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ yield await _stream()
+
+ async def get_orders(
+ self,
+ *,
+ search_input: typing.Optional[str] = None,
+ start_date: typing.Optional[dt.datetime] = None,
+ end_date: typing.Optional[dt.datetime] = None,
+ updated_start_date: typing.Optional[dt.datetime] = None,
+ updated_end_date: typing.Optional[dt.datetime] = None,
+ status: typing.Optional[typing.Union[OrderLowLevelStatus, typing.Sequence[OrderLowLevelStatus]]] = None,
+ order_key: typing.Optional[GetOrdersLabTestsRequestOrderKey] = None,
+ order_direction: typing.Optional[GetOrdersLabTestsRequestOrderDirection] = None,
+ order_type: typing.Optional[
+ typing.Union[LabTestCollectionMethod, typing.Sequence[LabTestCollectionMethod]]
+ ] = None,
+ is_critical: typing.Optional[bool] = None,
+ interpretation: typing.Optional[Interpretation] = None,
+ order_activation_types: typing.Optional[
+ typing.Union[OrderActivationType, typing.Sequence[OrderActivationType]]
+ ] = None,
+ user_id: typing.Optional[str] = None,
+ patient_name: typing.Optional[str] = None,
+ shipping_recipient_name: typing.Optional[str] = None,
+ order_ids: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None,
+ order_transaction_id: typing.Optional[str] = None,
+ page: typing.Optional[int] = None,
+ size: typing.Optional[int] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> AsyncHttpResponse[GetOrdersResponse]:
+ """
+ GET many orders with filters.
+
+ Parameters
+ ----------
+ search_input : typing.Optional[str]
+ Search by order id, user id, patient name, shipping dob, or shipping recipient name.
+
+ start_date : typing.Optional[dt.datetime]
+ Date from in YYYY-MM-DD or ISO formatted date time. If a date is provided without a time, the time will be set to 00:00:00
+
+ end_date : typing.Optional[dt.datetime]
+ Date to YYYY-MM-DD or ISO formatted date time. If a date is provided without a time, the time will be set to 23:59:59
+
+ updated_start_date : typing.Optional[dt.datetime]
+ Date from in YYYY-MM-DD or ISO formatted date time. If a date is provided without a time, the time will be set to 00:00:00
+
+ updated_end_date : typing.Optional[dt.datetime]
+ Date to YYYY-MM-DD or ISO formatted date time. If a date is provided without a time, the time will be set to 00:00:00
+
+ status : typing.Optional[typing.Union[OrderLowLevelStatus, typing.Sequence[OrderLowLevelStatus]]]
+ Filter by low level status.
+
+ order_key : typing.Optional[GetOrdersLabTestsRequestOrderKey]
+ Order key to sort by.
+
+ order_direction : typing.Optional[GetOrdersLabTestsRequestOrderDirection]
+ Order direction to sort by.
+
+ order_type : typing.Optional[typing.Union[LabTestCollectionMethod, typing.Sequence[LabTestCollectionMethod]]]
+ Filter by method used to perform the lab test.
+
+ is_critical : typing.Optional[bool]
+ Filter by critical order status.
+
+ interpretation : typing.Optional[Interpretation]
+ Filter by result interpretation of the lab test.
+
+ order_activation_types : typing.Optional[typing.Union[OrderActivationType, typing.Sequence[OrderActivationType]]]
+ Filter by activation type.
+
+ user_id : typing.Optional[str]
+ Filter by user ID.
+
+ patient_name : typing.Optional[str]
+ Filter by patient name.
+
+ shipping_recipient_name : typing.Optional[str]
+ Filter by shipping recipient name.
+
+ order_ids : typing.Optional[typing.Union[str, typing.Sequence[str]]]
+ Filter by order ids.
+
+ order_transaction_id : typing.Optional[str]
+ Filter by order transaction ID
+
+ page : typing.Optional[int]
+
+ size : typing.Optional[int]
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[GetOrdersResponse]
+ Successful Response
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ "v3/orders",
+ method="GET",
+ params={
+ "search_input": search_input,
+ "start_date": serialize_datetime(start_date) if start_date is not None else None,
+ "end_date": serialize_datetime(end_date) if end_date is not None else None,
+ "updated_start_date": serialize_datetime(updated_start_date)
+ if updated_start_date is not None
+ else None,
+ "updated_end_date": serialize_datetime(updated_end_date) if updated_end_date is not None else None,
+ "status": status,
+ "order_key": order_key,
+ "order_direction": order_direction,
+ "order_type": order_type,
+ "is_critical": is_critical,
+ "interpretation": interpretation,
+ "order_activation_types": order_activation_types,
+ "user_id": user_id,
+ "patient_name": patient_name,
+ "shipping_recipient_name": shipping_recipient_name,
+ "order_ids": order_ids,
+ "order_transaction_id": order_transaction_id,
+ "page": page,
+ "size": size,
+ },
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ GetOrdersResponse,
+ parse_obj_as(
+ type_=GetOrdersResponse, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 422:
+ raise UnprocessableEntityError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ HttpValidationError,
+ parse_obj_as(
+ type_=HttpValidationError, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ async def get_phlebotomy_appointment_availability(
+ self,
+ *,
+ first_line: str,
+ city: str,
+ state: str,
+ zip_code: str,
+ start_date: typing.Optional[str] = None,
+ second_line: typing.Optional[str] = OMIT,
+ unit: typing.Optional[str] = OMIT,
+ access_notes: typing.Optional[str] = OMIT,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> AsyncHttpResponse[AppointmentAvailabilitySlots]:
+ """
+ Return the available time slots to book an appointment with a phlebotomist
+ for the given address and order.
+
+ Parameters
+ ----------
+ first_line : str
+
+ city : str
+
+ state : str
+
+ zip_code : str
+
+ start_date : typing.Optional[str]
+ Start date for appointment availability
+
+ second_line : typing.Optional[str]
+
+ unit : typing.Optional[str]
+ Deprecated. Use `second_line` instead to provide the unit number. Subject to removal after 20 Nov 2023.
+
+ access_notes : typing.Optional[str]
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[AppointmentAvailabilitySlots]
+ Successful Response
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ "v3/order/phlebotomy/appointment/availability",
+ method="POST",
+ params={
+ "start_date": start_date,
+ },
+ json={
+ "first_line": first_line,
+ "second_line": second_line,
+ "city": city,
+ "state": state,
+ "zip_code": zip_code,
+ "unit": unit,
+ "access_notes": access_notes,
+ },
+ headers={
+ "content-type": "application/json",
+ },
+ request_options=request_options,
+ omit=OMIT,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ AppointmentAvailabilitySlots,
+ parse_obj_as(
+ type_=AppointmentAvailabilitySlots, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 422:
+ raise UnprocessableEntityError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ HttpValidationError,
+ parse_obj_as(
+ type_=HttpValidationError, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ async def book_phlebotomy_appointment(
+ self,
+ order_id: str,
+ *,
+ booking_key: str,
+ appointment_notes: typing.Optional[str] = OMIT,
+ async_confirmation: typing.Optional[bool] = OMIT,
+ sync_confirmation_timeout_millisecond: typing.Optional[int] = OMIT,
+ async_confirmation_timeout_millisecond: typing.Optional[int] = OMIT,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> AsyncHttpResponse[ClientFacingAppointment]:
+ """
+ Book an at-home phlebotomy appointment.
+
+ Parameters
+ ----------
+ order_id : str
+ Your Order ID.
+
+ booking_key : str
+
+ appointment_notes : typing.Optional[str]
+
+ async_confirmation : typing.Optional[bool]
+ If true, the endpoint attempts to confirm the booking within the `sync_confirmation_timeout_millisecond` window. If confirmation is not received in time, a pending appointment is returned and booking continues asynchronously. If false (default), the endpoint waits for confirmation or returns a 500 error on failure.
+
+ sync_confirmation_timeout_millisecond : typing.Optional[int]
+ Maximum time (in milliseconds) to wait for booking confirmation before returning a pending appointment. Only applies when `async_confirmation` is true. Defaults to 2500ms. Range: 1000-10000ms.
+
+ async_confirmation_timeout_millisecond : typing.Optional[int]
+ Maximum time (in milliseconds) to attempt asynchronous booking before cancelling the pending appointment. Only applies when `async_confirmation` is true. Defaults to 15 minutes. Range: 60000-172800000ms.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[ClientFacingAppointment]
+ Successful Response
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ f"v3/order/{encode_path_param(order_id)}/phlebotomy/appointment/book",
+ method="POST",
+ json={
+ "booking_key": booking_key,
+ "appointment_notes": appointment_notes,
+ "async_confirmation": async_confirmation,
+ "sync_confirmation_timeout_millisecond": sync_confirmation_timeout_millisecond,
+ "async_confirmation_timeout_millisecond": async_confirmation_timeout_millisecond,
+ },
+ headers={
+ "content-type": "application/json",
+ },
+ request_options=request_options,
+ omit=OMIT,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ ClientFacingAppointment,
+ parse_obj_as(
+ type_=ClientFacingAppointment, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 422:
+ raise UnprocessableEntityError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ HttpValidationError,
+ parse_obj_as(
+ type_=HttpValidationError, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ async def request_phlebotomy_appointment(
+ self,
+ order_id: str,
+ *,
+ address: UsAddress,
+ provider: AppointmentProvider,
+ appointment_notes: typing.Optional[str] = OMIT,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> AsyncHttpResponse[ClientFacingAppointment]:
+ """
+ Request an at-home phlebotomy appointment.
+
+ Parameters
+ ----------
+ order_id : str
+ Your Order ID.
+
+ address : UsAddress
+ At-home phlebotomy appointment address.
+
+ provider : AppointmentProvider
+ ℹ️ This enum is non-exhaustive.
+
+ appointment_notes : typing.Optional[str]
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[ClientFacingAppointment]
+ Successful Response
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ f"v3/order/{encode_path_param(order_id)}/phlebotomy/appointment/request",
+ method="POST",
+ json={
+ "address": address,
+ "provider": provider,
+ "appointment_notes": appointment_notes,
+ },
+ headers={
+ "content-type": "application/json",
+ },
+ request_options=request_options,
+ omit=OMIT,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ ClientFacingAppointment,
+ parse_obj_as(
+ type_=ClientFacingAppointment, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 422:
+ raise UnprocessableEntityError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ HttpValidationError,
+ parse_obj_as(
+ type_=HttpValidationError, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ async def reschedule_phlebotomy_appointment(
+ self,
+ order_id: str,
+ *,
+ booking_key: str,
+ appointment_notes: typing.Optional[str] = OMIT,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> AsyncHttpResponse[ClientFacingAppointment]:
+ """
+ Reschedule a previously booked at-home phlebotomy appointment.
+
+ Parameters
+ ----------
+ order_id : str
+ Your Order ID.
+
+ booking_key : str
+
+ appointment_notes : typing.Optional[str]
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[ClientFacingAppointment]
+ Successful Response
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ f"v3/order/{encode_path_param(order_id)}/phlebotomy/appointment/reschedule",
+ method="PATCH",
+ json={
+ "booking_key": booking_key,
+ "appointment_notes": appointment_notes,
+ },
+ headers={
+ "content-type": "application/json",
+ },
+ request_options=request_options,
+ omit=OMIT,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ ClientFacingAppointment,
+ parse_obj_as(
+ type_=ClientFacingAppointment, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 422:
+ raise UnprocessableEntityError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ HttpValidationError,
+ parse_obj_as(
+ type_=HttpValidationError, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ async def cancel_phlebotomy_appointment(
+ self,
+ order_id: str,
+ *,
+ cancellation_reason_id: str,
+ notes: typing.Optional[str] = OMIT,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> AsyncHttpResponse[ClientFacingAppointment]:
+ """
+ Cancel a previously booked at-home phlebotomy appointment.
+
+ Parameters
+ ----------
+ order_id : str
+ Your Order ID.
+
+ cancellation_reason_id : str
+
+ notes : typing.Optional[str]
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[ClientFacingAppointment]
+ Successful Response
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ f"v3/order/{encode_path_param(order_id)}/phlebotomy/appointment/cancel",
+ method="PATCH",
+ json={
+ "cancellation_reason_id": cancellation_reason_id,
+ "notes": notes,
+ },
headers={
"content-type": "application/json",
},
request_options=request_options,
- omit=OMIT,
+ omit=OMIT,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ ClientFacingAppointment,
+ parse_obj_as(
+ type_=ClientFacingAppointment, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 422:
+ raise UnprocessableEntityError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ HttpValidationError,
+ parse_obj_as(
+ type_=HttpValidationError, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ async def get_phlebotomy_appointment_cancellation_reason(
+ self, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> AsyncHttpResponse[typing.List[ClientFacingAppointmentCancellationReason]]:
+ """
+ Get the list of reasons for cancelling an at-home phlebotomy appointment.
+
+ Parameters
+ ----------
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[typing.List[ClientFacingAppointmentCancellationReason]]
+ Successful Response
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ "v3/order/phlebotomy/appointment/cancellation-reasons",
+ method="GET",
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ typing.List[ClientFacingAppointmentCancellationReason],
+ parse_obj_as(
+ type_=typing.List[ClientFacingAppointmentCancellationReason], # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return AsyncHttpResponse(response=_response, data=_data)
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ async def get_phlebotomy_appointment(
+ self, order_id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> AsyncHttpResponse[ClientFacingAppointment]:
+ """
+ Get the appointment associated with an order.
+
+ Parameters
+ ----------
+ order_id : str
+ Your Order ID.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[ClientFacingAppointment]
+ Successful Response
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ f"v3/order/{encode_path_param(order_id)}/phlebotomy/appointment",
+ method="GET",
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ ClientFacingAppointment,
+ parse_obj_as(
+ type_=ClientFacingAppointment, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 422:
+ raise UnprocessableEntityError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ HttpValidationError,
+ parse_obj_as(
+ type_=HttpValidationError, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ async def get_area_info(
+ self,
+ *,
+ zip_code: str,
+ radius: typing.Optional[AllowedRadius] = None,
+ lab: typing.Optional[ClientFacingLabs] = None,
+ labs: typing.Optional[typing.Union[ClientFacingLabs, typing.Sequence[ClientFacingLabs]]] = None,
+ lab_account_id: typing.Optional[str] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> AsyncHttpResponse[AreaInfo]:
+ """
+ GET information about an area with respect to lab-testing.
+
+ Information returned:
+ * Whether a given zip code is served by our Phlebotomy network.
+ * List of Lab locations in the area.
+
+ Parameters
+ ----------
+ zip_code : str
+ Zip code of the area to check
+
+ radius : typing.Optional[AllowedRadius]
+ Radius in which to search in miles
+
+ lab : typing.Optional[ClientFacingLabs]
+ Lab to check for PSCs
+
+ labs : typing.Optional[typing.Union[ClientFacingLabs, typing.Sequence[ClientFacingLabs]]]
+ List of labs to check for PSCs
+
+ lab_account_id : typing.Optional[str]
+ Lab Account ID to use for availability checks
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[AreaInfo]
+ Successful Response
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ "v3/order/area/info",
+ method="GET",
+ params={
+ "zip_code": zip_code,
+ "radius": radius,
+ "lab": lab,
+ "labs": labs,
+ "lab_account_id": lab_account_id,
+ },
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ AreaInfo,
+ parse_obj_as(
+ type_=AreaInfo, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 422:
+ raise UnprocessableEntityError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ HttpValidationError,
+ parse_obj_as(
+ type_=HttpValidationError, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ async def get_psc_info(
+ self,
+ *,
+ zip_code: str,
+ lab_id: int,
+ radius: typing.Optional[AllowedRadius] = None,
+ capabilities: typing.Optional[
+ typing.Union[LabLocationCapability, typing.Sequence[LabLocationCapability]]
+ ] = None,
+ lab_account_id: typing.Optional[str] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> AsyncHttpResponse[PscInfo]:
+ """
+ Parameters
+ ----------
+ zip_code : str
+ Zip code of the area to check
+
+ lab_id : int
+ Lab ID to check for PSCs
+
+ radius : typing.Optional[AllowedRadius]
+ Radius in which to search in miles. Note that we limit to 30 PSCs.
+
+ capabilities : typing.Optional[typing.Union[LabLocationCapability, typing.Sequence[LabLocationCapability]]]
+ Filter for only locations with certain capabilities
+
+ lab_account_id : typing.Optional[str]
+ Lab Account ID to use for availability checks
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[PscInfo]
+ Successful Response
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ "v3/order/psc/info",
+ method="GET",
+ params={
+ "zip_code": zip_code,
+ "lab_id": lab_id,
+ "radius": radius,
+ "capabilities": capabilities,
+ "lab_account_id": lab_account_id,
+ },
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ PscInfo,
+ parse_obj_as(
+ type_=PscInfo, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 422:
+ raise UnprocessableEntityError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ HttpValidationError,
+ parse_obj_as(
+ type_=HttpValidationError, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ async def get_order_psc_info(
+ self,
+ order_id: str,
+ *,
+ radius: typing.Optional[AllowedRadius] = None,
+ capabilities: typing.Optional[
+ typing.Union[LabLocationCapability, typing.Sequence[LabLocationCapability]]
+ ] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> AsyncHttpResponse[PscInfo]:
+ """
+ Parameters
+ ----------
+ order_id : str
+ Your Order ID.
+
+ radius : typing.Optional[AllowedRadius]
+ Radius in which to search in miles
+
+ capabilities : typing.Optional[typing.Union[LabLocationCapability, typing.Sequence[LabLocationCapability]]]
+ Filter for only locations with certain capabilities
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[PscInfo]
+ Successful Response
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ f"v3/order/{encode_path_param(order_id)}/psc/info",
+ method="GET",
+ params={
+ "radius": radius,
+ "capabilities": capabilities,
+ },
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ PscInfo,
+ parse_obj_as(
+ type_=PscInfo, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 422:
+ raise UnprocessableEntityError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ HttpValidationError,
+ parse_obj_as(
+ type_=HttpValidationError, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ @contextlib.asynccontextmanager
+ async def get_result_pdf(
+ self, order_id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[bytes]]]:
+ """
+ This endpoint returns the lab results for the order.
+
+ Parameters
+ ----------
+ order_id : str
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration. You can pass in configuration such as `chunk_size`, and more to customize the request and response.
+
+ Returns
+ -------
+ typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[bytes]]]
+ PDF with results
+ """
+ async with self._client_wrapper.httpx_client.stream(
+ f"v3/order/{encode_path_param(order_id)}/result/pdf",
+ method="GET",
+ request_options=request_options,
+ ) as _response:
+
+ async def _stream() -> AsyncHttpResponse[typing.AsyncIterator[bytes]]:
+ try:
+ if 200 <= _response.status_code < 300:
+ _chunk_size = request_options.get("chunk_size", None) if request_options is not None else None
+ return AsyncHttpResponse(
+ response=_response,
+ data=(_chunk async for _chunk in _response.aiter_bytes(chunk_size=_chunk_size)),
+ )
+ await _response.aread()
+ if _response.status_code == 422:
+ raise UnprocessableEntityError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ HttpValidationError,
+ parse_obj_as(
+ type_=HttpValidationError, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.text
+ )
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code,
+ headers=dict(_response.headers),
+ body=_response.json(),
+ cause=e,
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ yield await _stream()
+
+ async def get_result_metadata(
+ self, order_id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> AsyncHttpResponse[LabResultsMetadata]:
+ """
+ Return metadata related to order results, such as lab metadata,
+ provider and sample dates.
+
+ Parameters
+ ----------
+ order_id : str
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[LabResultsMetadata]
+ Successful Response
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ f"v3/order/{encode_path_param(order_id)}/result/metadata",
+ method="GET",
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ LabResultsMetadata,
+ parse_obj_as(
+ type_=LabResultsMetadata, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 422:
+ raise UnprocessableEntityError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ HttpValidationError,
+ parse_obj_as(
+ type_=HttpValidationError, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ async def get_result_raw(
+ self, order_id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> AsyncHttpResponse[LabResultsRaw]:
+ """
+ Return both metadata and raw json test data
+
+ Parameters
+ ----------
+ order_id : str
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[LabResultsRaw]
+ Successful Response
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ f"v3/order/{encode_path_param(order_id)}/result",
+ method="GET",
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ _data = typing.cast(
+ LabResultsRaw,
+ parse_obj_as(
+ type_=LabResultsRaw, # type: ignore
+ object_=_response.json(),
+ ),
+ )
+ return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 422:
+ raise UnprocessableEntityError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ HttpValidationError,
+ parse_obj_as(
+ type_=HttpValidationError, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ @contextlib.asynccontextmanager
+ async def get_labels_pdf(
+ self,
+ order_id: str,
+ *,
+ collection_date: dt.datetime,
+ number_of_labels: typing.Optional[int] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[bytes]]]:
+ """
+ This endpoint returns the printed labels for the order.
+
+ Parameters
+ ----------
+ order_id : str
+
+ collection_date : dt.datetime
+ Collection date
+
+ number_of_labels : typing.Optional[int]
+ Number of labels to generate
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration. You can pass in configuration such as `chunk_size`, and more to customize the request and response.
+
+ Returns
+ -------
+ typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[bytes]]]
+ PDF with labels
+ """
+ async with self._client_wrapper.httpx_client.stream(
+ f"v3/order/{encode_path_param(order_id)}/labels/pdf",
+ method="GET",
+ params={
+ "number_of_labels": number_of_labels,
+ "collection_date": serialize_datetime(collection_date),
+ },
+ request_options=request_options,
+ ) as _response:
+
+ async def _stream() -> AsyncHttpResponse[typing.AsyncIterator[bytes]]:
+ try:
+ if 200 <= _response.status_code < 300:
+ _chunk_size = request_options.get("chunk_size", None) if request_options is not None else None
+ return AsyncHttpResponse(
+ response=_response,
+ data=(_chunk async for _chunk in _response.aiter_bytes(chunk_size=_chunk_size)),
+ )
+ await _response.aread()
+ if _response.status_code == 422:
+ raise UnprocessableEntityError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ HttpValidationError,
+ parse_obj_as(
+ type_=HttpValidationError, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.text
+ )
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code,
+ headers=dict(_response.headers),
+ body=_response.json(),
+ cause=e,
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ yield await _stream()
+
+ async def get_psc_appointment_availability(
+ self,
+ *,
+ lab: AppointmentPscLabs,
+ start_date: typing.Optional[str] = None,
+ site_codes: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None,
+ zip_code: typing.Optional[str] = None,
+ radius: typing.Optional[AllowedRadius] = None,
+ allow_stale: typing.Optional[bool] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> AsyncHttpResponse[AppointmentAvailabilitySlots]:
+ """
+ Parameters
+ ----------
+ lab : AppointmentPscLabs
+ Lab to check for availability
+
+ start_date : typing.Optional[str]
+ Start date for appointment availability
+
+ site_codes : typing.Optional[typing.Union[str, typing.Sequence[str]]]
+ List of site codes to fetch availability for
+
+ zip_code : typing.Optional[str]
+ Zip code of the area to check
+
+ radius : typing.Optional[AllowedRadius]
+ Radius in which to search in miles
+
+ allow_stale : typing.Optional[bool]
+ If true, allows cached availability data to be returned.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[AppointmentAvailabilitySlots]
+ Successful Response
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ "v3/order/psc/appointment/availability",
+ method="POST",
+ params={
+ "lab": lab,
+ "start_date": start_date,
+ "site_codes": site_codes,
+ "zip_code": zip_code,
+ "radius": radius,
+ "allow_stale": allow_stale,
+ },
+ request_options=request_options,
)
try:
if 200 <= _response.status_code < 300:
_data = typing.cast(
- ClientFacingAppointment,
+ AppointmentAvailabilitySlots,
parse_obj_as(
- type_=ClientFacingAppointment, # type: ignore
+ type_=AppointmentAvailabilitySlots, # type: ignore
object_=_response.json(),
),
)
return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 404:
+ raise NotFoundError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ NotFoundErrorBody,
+ parse_obj_as(
+ type_=NotFoundErrorBody, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
if _response.status_code == 422:
raise UnprocessableEntityError(
headers=dict(_response.headers),
@@ -4624,37 +5795,91 @@ async def cancel_phlebotomy_appointment(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- async def get_phlebotomy_appointment_cancellation_reason(
- self, *, request_options: typing.Optional[RequestOptions] = None
- ) -> AsyncHttpResponse[typing.List[ClientFacingAppointmentCancellationReason]]:
+ async def book_psc_appointment(
+ self,
+ order_id: str,
+ *,
+ booking_key: str,
+ idempotency_key: typing.Optional[str] = None,
+ idempotency_error: typing.Optional[typing.Literal["no-cache"]] = None,
+ appointment_notes: typing.Optional[str] = OMIT,
+ async_confirmation: typing.Optional[bool] = OMIT,
+ sync_confirmation_timeout_millisecond: typing.Optional[int] = OMIT,
+ async_confirmation_timeout_millisecond: typing.Optional[int] = OMIT,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> AsyncHttpResponse[ClientFacingAppointment]:
"""
- Get the list of reasons for cancelling an at-home phlebotomy appointment.
+ Parameters
+ ----------
+ order_id : str
+ Your Order ID.
+
+ booking_key : str
+
+ idempotency_key : typing.Optional[str]
+ [!] This feature (Idempotency Key) is under closed beta. Idempotency Key support for booking PSC appointment.
+
+ idempotency_error : typing.Optional[typing.Literal["no-cache"]]
+ If `no-cache`, applies idempotency only to successful outcomes.
+
+ appointment_notes : typing.Optional[str]
+
+ async_confirmation : typing.Optional[bool]
+ If true, the endpoint attempts to confirm the booking within the `sync_confirmation_timeout_millisecond` window. If confirmation is not received in time, a pending appointment is returned and booking continues asynchronously. If false (default), the endpoint waits for confirmation or returns a 500 error on failure.
+
+ sync_confirmation_timeout_millisecond : typing.Optional[int]
+ Maximum time (in milliseconds) to wait for booking confirmation before returning a pending appointment. Only applies when `async_confirmation` is true. Defaults to 2500ms. Range: 1000-10000ms.
+
+ async_confirmation_timeout_millisecond : typing.Optional[int]
+ Maximum time (in milliseconds) to attempt asynchronous booking before cancelling the pending appointment. Only applies when `async_confirmation` is true. Defaults to 15 minutes. Range: 60000-172800000ms.
- Parameters
- ----------
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- AsyncHttpResponse[typing.List[ClientFacingAppointmentCancellationReason]]
+ AsyncHttpResponse[ClientFacingAppointment]
Successful Response
"""
_response = await self._client_wrapper.httpx_client.request(
- "v3/order/phlebotomy/appointment/cancellation-reasons",
- method="GET",
+ f"v3/order/{encode_path_param(order_id)}/psc/appointment/book",
+ method="POST",
+ json={
+ "booking_key": booking_key,
+ "appointment_notes": appointment_notes,
+ "async_confirmation": async_confirmation,
+ "sync_confirmation_timeout_millisecond": sync_confirmation_timeout_millisecond,
+ "async_confirmation_timeout_millisecond": async_confirmation_timeout_millisecond,
+ },
+ headers={
+ "content-type": "application/json",
+ "x-idempotency-key": str(idempotency_key) if idempotency_key is not None else None,
+ "x-idempotency-error": str(idempotency_error) if idempotency_error is not None else None,
+ },
request_options=request_options,
+ omit=OMIT,
)
try:
if 200 <= _response.status_code < 300:
_data = typing.cast(
- typing.List[ClientFacingAppointmentCancellationReason],
+ ClientFacingAppointment,
parse_obj_as(
- type_=typing.List[ClientFacingAppointmentCancellationReason], # type: ignore
+ type_=ClientFacingAppointment, # type: ignore
object_=_response.json(),
),
)
return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 422:
+ raise UnprocessableEntityError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ HttpValidationError,
+ parse_obj_as(
+ type_=HttpValidationError, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
_response_json = _response.json()
except JSONDecodeError:
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
@@ -4664,17 +5889,24 @@ async def get_phlebotomy_appointment_cancellation_reason(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- async def get_phlebotomy_appointment(
- self, order_id: str, *, request_options: typing.Optional[RequestOptions] = None
+ async def reschedule_psc_appointment(
+ self,
+ order_id: str,
+ *,
+ booking_key: str,
+ appointment_notes: typing.Optional[str] = OMIT,
+ request_options: typing.Optional[RequestOptions] = None,
) -> AsyncHttpResponse[ClientFacingAppointment]:
"""
- Get the appointment associated with an order.
-
Parameters
----------
order_id : str
Your Order ID.
+ booking_key : str
+
+ appointment_notes : typing.Optional[str]
+
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
@@ -4684,9 +5916,17 @@ async def get_phlebotomy_appointment(
Successful Response
"""
_response = await self._client_wrapper.httpx_client.request(
- f"v3/order/{encode_path_param(order_id)}/phlebotomy/appointment",
- method="GET",
+ f"v3/order/{encode_path_param(order_id)}/psc/appointment/reschedule",
+ method="PATCH",
+ json={
+ "booking_key": booking_key,
+ "appointment_notes": appointment_notes,
+ },
+ headers={
+ "content-type": "application/json",
+ },
request_options=request_options,
+ omit=OMIT,
)
try:
if 200 <= _response.status_code < 300:
@@ -4718,66 +5958,51 @@ async def get_phlebotomy_appointment(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- async def get_area_info(
+ async def cancel_psc_appointment(
self,
+ order_id: str,
*,
- zip_code: str,
- radius: typing.Optional[AllowedRadius] = None,
- lab: typing.Optional[ClientFacingLabs] = None,
- labs: typing.Optional[typing.Union[ClientFacingLabs, typing.Sequence[ClientFacingLabs]]] = None,
- lab_account_id: typing.Optional[str] = None,
+ cancellation_reason_id: str,
+ note: typing.Optional[str] = OMIT,
request_options: typing.Optional[RequestOptions] = None,
- ) -> AsyncHttpResponse[AreaInfo]:
+ ) -> AsyncHttpResponse[ClientFacingAppointment]:
"""
- GET information about an area with respect to lab-testing.
-
- Information returned:
- * Whether a given zip code is served by our Phlebotomy network.
- * List of Lab locations in the area.
-
Parameters
----------
- zip_code : str
- Zip code of the area to check
-
- radius : typing.Optional[AllowedRadius]
- Radius in which to search in miles
-
- lab : typing.Optional[ClientFacingLabs]
- Lab to check for PSCs
+ order_id : str
+ Your Order ID.
- labs : typing.Optional[typing.Union[ClientFacingLabs, typing.Sequence[ClientFacingLabs]]]
- List of labs to check for PSCs
+ cancellation_reason_id : str
- lab_account_id : typing.Optional[str]
- Lab Account ID to use for availability checks
+ note : typing.Optional[str]
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- AsyncHttpResponse[AreaInfo]
+ AsyncHttpResponse[ClientFacingAppointment]
Successful Response
"""
_response = await self._client_wrapper.httpx_client.request(
- "v3/order/area/info",
- method="GET",
- params={
- "zip_code": zip_code,
- "radius": radius,
- "lab": lab,
- "labs": labs,
- "lab_account_id": lab_account_id,
+ f"v3/order/{encode_path_param(order_id)}/psc/appointment/cancel",
+ method="PATCH",
+ json={
+ "cancellationReasonId": cancellation_reason_id,
+ "note": note,
+ },
+ headers={
+ "content-type": "application/json",
},
request_options=request_options,
+ omit=OMIT,
)
try:
if 200 <= _response.status_code < 300:
_data = typing.cast(
- AreaInfo,
+ ClientFacingAppointment,
parse_obj_as(
- type_=AreaInfo, # type: ignore
+ type_=ClientFacingAppointment, # type: ignore
object_=_response.json(),
),
)
@@ -4802,77 +6027,35 @@ async def get_area_info(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- async def get_psc_info(
- self,
- *,
- zip_code: str,
- lab_id: int,
- radius: typing.Optional[AllowedRadius] = None,
- capabilities: typing.Optional[
- typing.Union[LabLocationCapability, typing.Sequence[LabLocationCapability]]
- ] = None,
- lab_account_id: typing.Optional[str] = None,
- request_options: typing.Optional[RequestOptions] = None,
- ) -> AsyncHttpResponse[PscInfo]:
+ async def get_psc_appointment_cancellation_reason(
+ self, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> AsyncHttpResponse[typing.List[ClientFacingAppointmentCancellationReason]]:
"""
Parameters
----------
- zip_code : str
- Zip code of the area to check
-
- lab_id : int
- Lab ID to check for PSCs
-
- radius : typing.Optional[AllowedRadius]
- Radius in which to search in miles. Note that we limit to 30 PSCs.
-
- capabilities : typing.Optional[typing.Union[LabLocationCapability, typing.Sequence[LabLocationCapability]]]
- Filter for only locations with certain capabilities
-
- lab_account_id : typing.Optional[str]
- Lab Account ID to use for availability checks
-
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- AsyncHttpResponse[PscInfo]
+ AsyncHttpResponse[typing.List[ClientFacingAppointmentCancellationReason]]
Successful Response
"""
_response = await self._client_wrapper.httpx_client.request(
- "v3/order/psc/info",
+ "v3/order/psc/appointment/cancellation-reasons",
method="GET",
- params={
- "zip_code": zip_code,
- "lab_id": lab_id,
- "radius": radius,
- "capabilities": capabilities,
- "lab_account_id": lab_account_id,
- },
request_options=request_options,
)
try:
if 200 <= _response.status_code < 300:
_data = typing.cast(
- PscInfo,
+ typing.List[ClientFacingAppointmentCancellationReason],
parse_obj_as(
- type_=PscInfo, # type: ignore
+ type_=typing.List[ClientFacingAppointmentCancellationReason], # type: ignore
object_=_response.json(),
),
)
return AsyncHttpResponse(response=_response, data=_data)
- if _response.status_code == 422:
- raise UnprocessableEntityError(
- headers=dict(_response.headers),
- body=typing.cast(
- HttpValidationError,
- parse_obj_as(
- type_=HttpValidationError, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
_response_json = _response.json()
except JSONDecodeError:
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
@@ -4882,51 +6065,36 @@ async def get_psc_info(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- async def get_order_psc_info(
- self,
- order_id: str,
- *,
- radius: typing.Optional[AllowedRadius] = None,
- capabilities: typing.Optional[
- typing.Union[LabLocationCapability, typing.Sequence[LabLocationCapability]]
- ] = None,
- request_options: typing.Optional[RequestOptions] = None,
- ) -> AsyncHttpResponse[PscInfo]:
+ async def get_psc_appointment(
+ self, order_id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> AsyncHttpResponse[ClientFacingAppointment]:
"""
+ Get the appointment associated with an order.
+
Parameters
----------
order_id : str
Your Order ID.
- radius : typing.Optional[AllowedRadius]
- Radius in which to search in miles
-
- capabilities : typing.Optional[typing.Union[LabLocationCapability, typing.Sequence[LabLocationCapability]]]
- Filter for only locations with certain capabilities
-
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- AsyncHttpResponse[PscInfo]
+ AsyncHttpResponse[ClientFacingAppointment]
Successful Response
"""
_response = await self._client_wrapper.httpx_client.request(
- f"v3/order/{encode_path_param(order_id)}/psc/info",
+ f"v3/order/{encode_path_param(order_id)}/psc/appointment",
method="GET",
- params={
- "radius": radius,
- "capabilities": capabilities,
- },
request_options=request_options,
)
try:
if 200 <= _response.status_code < 300:
_data = typing.cast(
- PscInfo,
+ ClientFacingAppointment,
parse_obj_as(
- type_=PscInfo, # type: ignore
+ type_=ClientFacingAppointment, # type: ignore
object_=_response.json(),
),
)
@@ -4952,15 +6120,78 @@ async def get_order_psc_info(
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
@contextlib.asynccontextmanager
- async def get_result_pdf(
+ async def get_order_collection_instruction_pdf(
+ self, order_id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[bytes]]]:
+ """
+ GET collection instructions for an order
+
+ Parameters
+ ----------
+ order_id : str
+ Your Order ID.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration. You can pass in configuration such as `chunk_size`, and more to customize the request and response.
+
+ Returns
+ -------
+ typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[bytes]]]
+ PDF with collection instructions
+ """
+ async with self._client_wrapper.httpx_client.stream(
+ f"v3/order/{encode_path_param(order_id)}/collection_instruction_pdf",
+ method="GET",
+ request_options=request_options,
+ ) as _response:
+
+ async def _stream() -> AsyncHttpResponse[typing.AsyncIterator[bytes]]:
+ try:
+ if 200 <= _response.status_code < 300:
+ _chunk_size = request_options.get("chunk_size", None) if request_options is not None else None
+ return AsyncHttpResponse(
+ response=_response,
+ data=(_chunk async for _chunk in _response.aiter_bytes(chunk_size=_chunk_size)),
+ )
+ await _response.aread()
+ if _response.status_code == 422:
+ raise UnprocessableEntityError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ HttpValidationError,
+ parse_obj_as(
+ type_=HttpValidationError, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.text
+ )
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code,
+ headers=dict(_response.headers),
+ body=_response.json(),
+ cause=e,
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ yield await _stream()
+
+ @contextlib.asynccontextmanager
+ async def get_order_requistion_pdf(
self, order_id: str, *, request_options: typing.Optional[RequestOptions] = None
) -> typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[bytes]]]:
"""
- This endpoint returns the lab results for the order.
+ GET requisition pdf for an order
Parameters
----------
order_id : str
+ Your Order ID.
request_options : typing.Optional[RequestOptions]
Request-specific configuration. You can pass in configuration such as `chunk_size`, and more to customize the request and response.
@@ -4968,10 +6199,10 @@ async def get_result_pdf(
Returns
-------
typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[bytes]]]
- PDF with results
+ PDF with requisition form
"""
async with self._client_wrapper.httpx_client.stream(
- f"v3/order/{encode_path_param(order_id)}/result/pdf",
+ f"v3/order/{encode_path_param(order_id)}/requisition/pdf",
method="GET",
request_options=request_options,
) as _response:
@@ -5012,89 +6243,98 @@ async def _stream() -> AsyncHttpResponse[typing.AsyncIterator[bytes]]:
yield await _stream()
- async def get_result_metadata(
+ @contextlib.asynccontextmanager
+ async def get_order_abn_pdf(
self, order_id: str, *, request_options: typing.Optional[RequestOptions] = None
- ) -> AsyncHttpResponse[LabResultsMetadata]:
+ ) -> typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[bytes]]]:
"""
- Return metadata related to order results, such as lab metadata,
- provider and sample dates.
+ GET ABN pdf for an order
Parameters
----------
order_id : str
+ Your Order ID.
request_options : typing.Optional[RequestOptions]
- Request-specific configuration.
+ Request-specific configuration. You can pass in configuration such as `chunk_size`, and more to customize the request and response.
Returns
-------
- AsyncHttpResponse[LabResultsMetadata]
- Successful Response
+ typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[bytes]]]
+ PDF with ABN form
"""
- _response = await self._client_wrapper.httpx_client.request(
- f"v3/order/{encode_path_param(order_id)}/result/metadata",
+ async with self._client_wrapper.httpx_client.stream(
+ f"v3/order/{encode_path_param(order_id)}/abn_pdf",
method="GET",
request_options=request_options,
- )
- try:
- if 200 <= _response.status_code < 300:
- _data = typing.cast(
- LabResultsMetadata,
- parse_obj_as(
- type_=LabResultsMetadata, # type: ignore
- object_=_response.json(),
- ),
- )
- return AsyncHttpResponse(response=_response, data=_data)
- if _response.status_code == 422:
- raise UnprocessableEntityError(
- headers=dict(_response.headers),
- body=typing.cast(
- HttpValidationError,
- parse_obj_as(
- type_=HttpValidationError, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- _response_json = _response.json()
- except JSONDecodeError:
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
- except ValidationError as e:
- raise ParsingError(
- status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
- )
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+ ) as _response:
- async def get_result_raw(
+ async def _stream() -> AsyncHttpResponse[typing.AsyncIterator[bytes]]:
+ try:
+ if 200 <= _response.status_code < 300:
+ _chunk_size = request_options.get("chunk_size", None) if request_options is not None else None
+ return AsyncHttpResponse(
+ response=_response,
+ data=(_chunk async for _chunk in _response.aiter_bytes(chunk_size=_chunk_size)),
+ )
+ await _response.aread()
+ if _response.status_code == 422:
+ raise UnprocessableEntityError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ HttpValidationError,
+ parse_obj_as(
+ type_=HttpValidationError, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.text
+ )
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code,
+ headers=dict(_response.headers),
+ body=_response.json(),
+ cause=e,
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
+ yield await _stream()
+
+ async def get_order(
self, order_id: str, *, request_options: typing.Optional[RequestOptions] = None
- ) -> AsyncHttpResponse[LabResultsRaw]:
+ ) -> AsyncHttpResponse[ClientFacingOrder]:
"""
- Return both metadata and raw json test data
+ GET individual order by ID.
Parameters
----------
order_id : str
+ Your Order ID.
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- AsyncHttpResponse[LabResultsRaw]
+ AsyncHttpResponse[ClientFacingOrder]
Successful Response
"""
_response = await self._client_wrapper.httpx_client.request(
- f"v3/order/{encode_path_param(order_id)}/result",
+ f"v3/order/{encode_path_param(order_id)}",
method="GET",
request_options=request_options,
)
try:
if 200 <= _response.status_code < 300:
_data = typing.cast(
- LabResultsRaw,
+ ClientFacingOrder,
parse_obj_as(
- type_=LabResultsRaw, # type: ignore
+ type_=ClientFacingOrder, # type: ignore
object_=_response.json(),
),
)
@@ -5119,156 +6359,64 @@ async def get_result_raw(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- @contextlib.asynccontextmanager
- async def get_labels_pdf(
+ async def update_order(
self,
order_id: str,
*,
- collection_date: dt.datetime,
- number_of_labels: typing.Optional[int] = None,
+ activate_by: typing.Optional[str] = OMIT,
request_options: typing.Optional[RequestOptions] = None,
- ) -> typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[bytes]]]:
- """
- This endpoint returns the printed labels for the order.
-
- Parameters
- ----------
- order_id : str
-
- collection_date : dt.datetime
- Collection date
-
- number_of_labels : typing.Optional[int]
- Number of labels to generate
-
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration. You can pass in configuration such as `chunk_size`, and more to customize the request and response.
-
- Returns
- -------
- typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[bytes]]]
- PDF with labels
+ ) -> AsyncHttpResponse[PostOrderResponse]:
"""
- async with self._client_wrapper.httpx_client.stream(
- f"v3/order/{encode_path_param(order_id)}/labels/pdf",
- method="GET",
- params={
- "number_of_labels": number_of_labels,
- "collection_date": serialize_datetime(collection_date),
- },
- request_options=request_options,
- ) as _response:
+ Update a modifiable order's scheduled activation date.
- async def _stream() -> AsyncHttpResponse[typing.AsyncIterator[bytes]]:
- try:
- if 200 <= _response.status_code < 300:
- _chunk_size = request_options.get("chunk_size", None) if request_options is not None else None
- return AsyncHttpResponse(
- response=_response,
- data=(_chunk async for _chunk in _response.aiter_bytes(chunk_size=_chunk_size)),
- )
- await _response.aread()
- if _response.status_code == 422:
- raise UnprocessableEntityError(
- headers=dict(_response.headers),
- body=typing.cast(
- HttpValidationError,
- parse_obj_as(
- type_=HttpValidationError, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- _response_json = _response.json()
- except JSONDecodeError:
- raise ApiError(
- status_code=_response.status_code, headers=dict(_response.headers), body=_response.text
- )
- except ValidationError as e:
- raise ParsingError(
- status_code=_response.status_code,
- headers=dict(_response.headers),
- body=_response.json(),
- cause=e,
- )
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+ The order must be in `ordered` or `awaiting_registration` status. Setting
+ `activate_by` to a future date reschedules dispatch; setting it to `null`
+ clears the schedule and enqueues immediate dispatch for `ordered` orders.
- yield await _stream()
+ Returns 400 when:
+ - the order is not in a modifiable status,
+ - the order was created for immediate processing (cannot be scheduled
+ after the fact),
+ - `activate_by` is in the past.
- async def get_psc_appointment_availability(
- self,
- *,
- lab: AppointmentPscLabs,
- start_date: typing.Optional[str] = None,
- site_codes: typing.Optional[typing.Union[str, typing.Sequence[str]]] = None,
- zip_code: typing.Optional[str] = None,
- radius: typing.Optional[AllowedRadius] = None,
- allow_stale: typing.Optional[bool] = None,
- request_options: typing.Optional[RequestOptions] = None,
- ) -> AsyncHttpResponse[AppointmentAvailabilitySlots]:
- """
Parameters
----------
- lab : AppointmentPscLabs
- Lab to check for availability
-
- start_date : typing.Optional[str]
- Start date for appointment availability
-
- site_codes : typing.Optional[typing.Union[str, typing.Sequence[str]]]
- List of site codes to fetch availability for
-
- zip_code : typing.Optional[str]
- Zip code of the area to check
-
- radius : typing.Optional[AllowedRadius]
- Radius in which to search in miles
+ order_id : str
+ Your Order ID.
- allow_stale : typing.Optional[bool]
- If true, allows cached availability data to be returned.
+ activate_by : typing.Optional[str]
+ The date on which the order should be activated (dispatched to the lab). Must be today or a future date. Set to `null` to clear an existing scheduled date and dispatch the order immediately. Note: an order originally created for immediate processing (no `activate_by` at creation time) cannot be rescheduled — only orders that were created with an `activate_by` can have it changed or cleared.
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- AsyncHttpResponse[AppointmentAvailabilitySlots]
+ AsyncHttpResponse[PostOrderResponse]
Successful Response
"""
_response = await self._client_wrapper.httpx_client.request(
- "v3/order/psc/appointment/availability",
- method="POST",
- params={
- "lab": lab,
- "start_date": start_date,
- "site_codes": site_codes,
- "zip_code": zip_code,
- "radius": radius,
- "allow_stale": allow_stale,
+ f"v3/order/{encode_path_param(order_id)}",
+ method="PATCH",
+ json={
+ "activate_by": activate_by,
+ },
+ headers={
+ "content-type": "application/json",
},
request_options=request_options,
+ omit=OMIT,
)
try:
if 200 <= _response.status_code < 300:
_data = typing.cast(
- AppointmentAvailabilitySlots,
+ PostOrderResponse,
parse_obj_as(
- type_=AppointmentAvailabilitySlots, # type: ignore
+ type_=PostOrderResponse, # type: ignore
object_=_response.json(),
),
)
return AsyncHttpResponse(response=_response, data=_data)
- if _response.status_code == 404:
- raise NotFoundError(
- headers=dict(_response.headers),
- body=typing.cast(
- NotFoundErrorBody,
- parse_obj_as(
- type_=NotFoundErrorBody, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
if _response.status_code == 422:
raise UnprocessableEntityError(
headers=dict(_response.headers),
@@ -5289,66 +6437,113 @@ async def get_psc_appointment_availability(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- async def book_psc_appointment(
+ async def create_order(
self,
- order_id: str,
*,
- booking_key: str,
+ user_id: str,
+ patient_details: PatientDetailsWithValidation,
+ patient_address: PatientAddressWithValidation,
idempotency_key: typing.Optional[str] = None,
idempotency_error: typing.Optional[typing.Literal["no-cache"]] = None,
- appointment_notes: typing.Optional[str] = OMIT,
- async_confirmation: typing.Optional[bool] = OMIT,
- sync_confirmation_timeout_millisecond: typing.Optional[int] = OMIT,
- async_confirmation_timeout_millisecond: typing.Optional[int] = OMIT,
+ lab_test_id: typing.Optional[str] = OMIT,
+ order_set: typing.Optional[OrderSetRequest] = OMIT,
+ collection_method: typing.Optional[LabTestCollectionMethod] = OMIT,
+ physician: typing.Optional[PhysicianCreateRequest] = OMIT,
+ health_insurance: typing.Optional[HealthInsuranceCreateRequest] = OMIT,
+ priority: typing.Optional[bool] = OMIT,
+ billing_type: typing.Optional[Billing] = OMIT,
+ icd_codes: typing.Optional[typing.Sequence[str]] = OMIT,
+ consents: typing.Optional[typing.Sequence[Consent]] = OMIT,
+ activate_by: typing.Optional[str] = OMIT,
+ aoe_answers: typing.Optional[typing.Sequence[AoEAnswer]] = OMIT,
+ passthrough: typing.Optional[str] = OMIT,
+ clinical_notes: typing.Optional[str] = OMIT,
+ lab_account_id: typing.Optional[str] = OMIT,
+ creator_member_id: typing.Optional[str] = OMIT,
request_options: typing.Optional[RequestOptions] = None,
- ) -> AsyncHttpResponse[ClientFacingAppointment]:
+ ) -> AsyncHttpResponse[PostOrderResponse]:
"""
Parameters
----------
- order_id : str
- Your Order ID.
+ user_id : str
- booking_key : str
+ patient_details : PatientDetailsWithValidation
+
+ patient_address : PatientAddressWithValidation
idempotency_key : typing.Optional[str]
- [!] This feature (Idempotency Key) is under closed beta. Idempotency Key support for booking PSC appointment.
idempotency_error : typing.Optional[typing.Literal["no-cache"]]
- If `no-cache`, applies idempotency only to successful outcomes.
- appointment_notes : typing.Optional[str]
+ lab_test_id : typing.Optional[str]
- async_confirmation : typing.Optional[bool]
- If true, the endpoint attempts to confirm the booking within the `sync_confirmation_timeout_millisecond` window. If confirmation is not received in time, a pending appointment is returned and booking continues asynchronously. If false (default), the endpoint waits for confirmation or returns a 500 error on failure.
+ order_set : typing.Optional[OrderSetRequest]
- sync_confirmation_timeout_millisecond : typing.Optional[int]
- Maximum time (in milliseconds) to wait for booking confirmation before returning a pending appointment. Only applies when `async_confirmation` is true. Defaults to 2500ms. Range: 1000-10000ms.
+ collection_method : typing.Optional[LabTestCollectionMethod]
+ ℹ️ This enum is non-exhaustive.
- async_confirmation_timeout_millisecond : typing.Optional[int]
- Maximum time (in milliseconds) to attempt asynchronous booking before cancelling the pending appointment. Only applies when `async_confirmation` is true. Defaults to 15 minutes. Range: 60000-172800000ms.
+ physician : typing.Optional[PhysicianCreateRequest]
+
+ health_insurance : typing.Optional[HealthInsuranceCreateRequest]
+
+ priority : typing.Optional[bool]
+ Defines whether order is priority or not. For some labs, this refers to a STAT order.
+
+ billing_type : typing.Optional[Billing]
+ ℹ️ This enum is non-exhaustive.
+
+ icd_codes : typing.Optional[typing.Sequence[str]]
+
+ consents : typing.Optional[typing.Sequence[Consent]]
+
+ activate_by : typing.Optional[str]
+ Schedule an Order to be processed in a future date.
+
+ aoe_answers : typing.Optional[typing.Sequence[AoEAnswer]]
+
+ passthrough : typing.Optional[str]
+
+ clinical_notes : typing.Optional[str]
+
+ lab_account_id : typing.Optional[str]
+
+ creator_member_id : typing.Optional[str]
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- AsyncHttpResponse[ClientFacingAppointment]
+ AsyncHttpResponse[PostOrderResponse]
Successful Response
"""
_response = await self._client_wrapper.httpx_client.request(
- f"v3/order/{encode_path_param(order_id)}/psc/appointment/book",
+ "v3/order",
method="POST",
json={
- "booking_key": booking_key,
- "appointment_notes": appointment_notes,
- "async_confirmation": async_confirmation,
- "sync_confirmation_timeout_millisecond": sync_confirmation_timeout_millisecond,
- "async_confirmation_timeout_millisecond": async_confirmation_timeout_millisecond,
+ "user_id": user_id,
+ "lab_test_id": lab_test_id,
+ "order_set": order_set,
+ "collection_method": collection_method,
+ "physician": physician,
+ "health_insurance": health_insurance,
+ "priority": priority,
+ "billing_type": billing_type,
+ "icd_codes": icd_codes,
+ "consents": consents,
+ "activate_by": activate_by,
+ "aoe_answers": aoe_answers,
+ "passthrough": passthrough,
+ "clinical_notes": clinical_notes,
+ "lab_account_id": lab_account_id,
+ "creator_member_id": creator_member_id,
+ "patient_details": patient_details,
+ "patient_address": patient_address,
},
headers={
"content-type": "application/json",
- "x-idempotency-key": str(idempotency_key) if idempotency_key is not None else None,
- "x-idempotency-error": str(idempotency_error) if idempotency_error is not None else None,
+ "X-Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None,
+ "X-Idempotency-Error": str(idempotency_error) if idempotency_error is not None else None,
},
request_options=request_options,
omit=OMIT,
@@ -5356,9 +6551,9 @@ async def book_psc_appointment(
try:
if 200 <= _response.status_code < 300:
_data = typing.cast(
- ClientFacingAppointment,
+ PostOrderResponse,
parse_obj_as(
- type_=ClientFacingAppointment, # type: ignore
+ type_=PostOrderResponse, # type: ignore
object_=_response.json(),
),
)
@@ -5383,38 +6578,64 @@ async def book_psc_appointment(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- async def reschedule_psc_appointment(
+ async def import_order(
self,
- order_id: str,
*,
- booking_key: str,
- appointment_notes: typing.Optional[str] = OMIT,
+ user_id: str,
+ billing_type: Billing,
+ order_set: OrderSetRequest,
+ collection_method: LabTestCollectionMethod,
+ patient_details: PatientDetailsWithValidation,
+ patient_address: PatientAddress,
+ sample_id: str,
+ physician: typing.Optional[PhysicianCreateRequest] = OMIT,
+ lab_account_id: typing.Optional[str] = OMIT,
request_options: typing.Optional[RequestOptions] = None,
- ) -> AsyncHttpResponse[ClientFacingAppointment]:
+ ) -> AsyncHttpResponse[PostOrderResponse]:
"""
Parameters
----------
- order_id : str
- Your Order ID.
+ user_id : str
- booking_key : str
+ billing_type : Billing
+ ℹ️ This enum is non-exhaustive.
- appointment_notes : typing.Optional[str]
+ order_set : OrderSetRequest
+
+ collection_method : LabTestCollectionMethod
+ ℹ️ This enum is non-exhaustive.
+
+ patient_details : PatientDetailsWithValidation
+
+ patient_address : PatientAddress
+
+ sample_id : str
+
+ physician : typing.Optional[PhysicianCreateRequest]
+
+ lab_account_id : typing.Optional[str]
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- AsyncHttpResponse[ClientFacingAppointment]
+ AsyncHttpResponse[PostOrderResponse]
Successful Response
"""
_response = await self._client_wrapper.httpx_client.request(
- f"v3/order/{encode_path_param(order_id)}/psc/appointment/reschedule",
- method="PATCH",
+ "v3/order/import",
+ method="POST",
json={
- "booking_key": booking_key,
- "appointment_notes": appointment_notes,
+ "user_id": user_id,
+ "billing_type": billing_type,
+ "order_set": order_set,
+ "collection_method": collection_method,
+ "physician": physician,
+ "patient_details": patient_details,
+ "patient_address": patient_address,
+ "sample_id": sample_id,
+ "lab_account_id": lab_account_id,
},
headers={
"content-type": "application/json",
@@ -5425,9 +6646,9 @@ async def reschedule_psc_appointment(
try:
if 200 <= _response.status_code < 300:
_data = typing.cast(
- ClientFacingAppointment,
+ PostOrderResponse,
parse_obj_as(
- type_=ClientFacingAppointment, # type: ignore
+ type_=PostOrderResponse, # type: ignore
object_=_response.json(),
),
)
@@ -5452,51 +6673,36 @@ async def reschedule_psc_appointment(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- async def cancel_psc_appointment(
- self,
- order_id: str,
- *,
- cancellation_reason_id: str,
- note: typing.Optional[str] = OMIT,
- request_options: typing.Optional[RequestOptions] = None,
- ) -> AsyncHttpResponse[ClientFacingAppointment]:
+ async def cancel_order(
+ self, order_id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> AsyncHttpResponse[PostOrderResponse]:
"""
+ POST cancel order
+
Parameters
----------
order_id : str
Your Order ID.
- cancellation_reason_id : str
-
- note : typing.Optional[str]
-
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- AsyncHttpResponse[ClientFacingAppointment]
+ AsyncHttpResponse[PostOrderResponse]
Successful Response
"""
_response = await self._client_wrapper.httpx_client.request(
- f"v3/order/{encode_path_param(order_id)}/psc/appointment/cancel",
- method="PATCH",
- json={
- "cancellationReasonId": cancellation_reason_id,
- "note": note,
- },
- headers={
- "content-type": "application/json",
- },
+ f"v3/order/{encode_path_param(order_id)}/cancel",
+ method="POST",
request_options=request_options,
- omit=OMIT,
)
try:
if 200 <= _response.status_code < 300:
_data = typing.cast(
- ClientFacingAppointment,
+ PostOrderResponse,
parse_obj_as(
- type_=ClientFacingAppointment, # type: ignore
+ type_=PostOrderResponse, # type: ignore
object_=_response.json(),
),
)
@@ -5521,35 +6727,73 @@ async def cancel_psc_appointment(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- async def get_psc_appointment_cancellation_reason(
- self, *, request_options: typing.Optional[RequestOptions] = None
- ) -> AsyncHttpResponse[typing.List[ClientFacingAppointmentCancellationReason]]:
+ async def simulate_order_process(
+ self,
+ order_id: str,
+ *,
+ final_status: typing.Optional[OrderStatus] = None,
+ delay: typing.Optional[int] = None,
+ request: typing.Optional[SimulationFlags] = None,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> AsyncHttpResponse[typing.Any]:
"""
+ Get available test kits.
+
Parameters
----------
+ order_id : str
+
+ final_status : typing.Optional[OrderStatus]
+
+ delay : typing.Optional[int]
+
+ request : typing.Optional[SimulationFlags]
+
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- AsyncHttpResponse[typing.List[ClientFacingAppointmentCancellationReason]]
+ AsyncHttpResponse[typing.Any]
Successful Response
"""
_response = await self._client_wrapper.httpx_client.request(
- "v3/order/psc/appointment/cancellation-reasons",
- method="GET",
+ f"v3/order/{encode_path_param(order_id)}/test",
+ method="POST",
+ params={
+ "final_status": final_status,
+ "delay": delay,
+ },
+ json=request,
+ headers={
+ "content-type": "application/json",
+ },
request_options=request_options,
+ omit=OMIT,
)
try:
+ if _response is None or not _response.text.strip():
+ return AsyncHttpResponse(response=_response, data=None)
if 200 <= _response.status_code < 300:
_data = typing.cast(
- typing.List[ClientFacingAppointmentCancellationReason],
+ typing.Any,
parse_obj_as(
- type_=typing.List[ClientFacingAppointmentCancellationReason], # type: ignore
+ type_=typing.Any, # type: ignore
object_=_response.json(),
),
)
return AsyncHttpResponse(response=_response, data=_data)
+ if _response.status_code == 422:
+ raise UnprocessableEntityError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ HttpValidationError,
+ parse_obj_as(
+ type_=HttpValidationError, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
_response_json = _response.json()
except JSONDecodeError:
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
@@ -5559,11 +6803,11 @@ async def get_psc_appointment_cancellation_reason(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- async def get_psc_appointment(
+ async def update_on_site_collection_order_draw_completed(
self, order_id: str, *, request_options: typing.Optional[RequestOptions] = None
- ) -> AsyncHttpResponse[ClientFacingAppointment]:
+ ) -> AsyncHttpResponse[PostOrderResponse]:
"""
- Get the appointment associated with an order.
+ PATCH update on site collection order when draw is completed
Parameters
----------
@@ -5575,20 +6819,20 @@ async def get_psc_appointment(
Returns
-------
- AsyncHttpResponse[ClientFacingAppointment]
+ AsyncHttpResponse[PostOrderResponse]
Successful Response
"""
_response = await self._client_wrapper.httpx_client.request(
- f"v3/order/{encode_path_param(order_id)}/psc/appointment",
- method="GET",
+ f"v3/order/{encode_path_param(order_id)}/draw_completed",
+ method="PATCH",
request_options=request_options,
)
try:
if 200 <= _response.status_code < 300:
_data = typing.cast(
- ClientFacingAppointment,
+ PostOrderResponse,
parse_obj_as(
- type_=ClientFacingAppointment, # type: ignore
+ type_=PostOrderResponse, # type: ignore
object_=_response.json(),
),
)
@@ -5613,237 +6857,35 @@ async def get_psc_appointment(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- @contextlib.asynccontextmanager
- async def get_order_collection_instruction_pdf(
- self, order_id: str, *, request_options: typing.Optional[RequestOptions] = None
- ) -> typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[bytes]]]:
- """
- GET collection instructions for an order
-
- Parameters
- ----------
- order_id : str
- Your Order ID.
-
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration. You can pass in configuration such as `chunk_size`, and more to customize the request and response.
-
- Returns
- -------
- typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[bytes]]]
- PDF with collection instructions
- """
- async with self._client_wrapper.httpx_client.stream(
- f"v3/order/{encode_path_param(order_id)}/collection_instruction_pdf",
- method="GET",
- request_options=request_options,
- ) as _response:
-
- async def _stream() -> AsyncHttpResponse[typing.AsyncIterator[bytes]]:
- try:
- if 200 <= _response.status_code < 300:
- _chunk_size = request_options.get("chunk_size", None) if request_options is not None else None
- return AsyncHttpResponse(
- response=_response,
- data=(_chunk async for _chunk in _response.aiter_bytes(chunk_size=_chunk_size)),
- )
- await _response.aread()
- if _response.status_code == 422:
- raise UnprocessableEntityError(
- headers=dict(_response.headers),
- body=typing.cast(
- HttpValidationError,
- parse_obj_as(
- type_=HttpValidationError, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- _response_json = _response.json()
- except JSONDecodeError:
- raise ApiError(
- status_code=_response.status_code, headers=dict(_response.headers), body=_response.text
- )
- except ValidationError as e:
- raise ParsingError(
- status_code=_response.status_code,
- headers=dict(_response.headers),
- body=_response.json(),
- cause=e,
- )
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
-
- yield await _stream()
-
- @contextlib.asynccontextmanager
- async def get_order_requistion_pdf(
- self, order_id: str, *, request_options: typing.Optional[RequestOptions] = None
- ) -> typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[bytes]]]:
- """
- GET requisition pdf for an order
-
- Parameters
- ----------
- order_id : str
- Your Order ID.
-
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration. You can pass in configuration such as `chunk_size`, and more to customize the request and response.
-
- Returns
- -------
- typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[bytes]]]
- PDF with requisition form
- """
- async with self._client_wrapper.httpx_client.stream(
- f"v3/order/{encode_path_param(order_id)}/requisition/pdf",
- method="GET",
- request_options=request_options,
- ) as _response:
-
- async def _stream() -> AsyncHttpResponse[typing.AsyncIterator[bytes]]:
- try:
- if 200 <= _response.status_code < 300:
- _chunk_size = request_options.get("chunk_size", None) if request_options is not None else None
- return AsyncHttpResponse(
- response=_response,
- data=(_chunk async for _chunk in _response.aiter_bytes(chunk_size=_chunk_size)),
- )
- await _response.aread()
- if _response.status_code == 422:
- raise UnprocessableEntityError(
- headers=dict(_response.headers),
- body=typing.cast(
- HttpValidationError,
- parse_obj_as(
- type_=HttpValidationError, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- _response_json = _response.json()
- except JSONDecodeError:
- raise ApiError(
- status_code=_response.status_code, headers=dict(_response.headers), body=_response.text
- )
- except ValidationError as e:
- raise ParsingError(
- status_code=_response.status_code,
- headers=dict(_response.headers),
- body=_response.json(),
- cause=e,
- )
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
-
- yield await _stream()
-
- @contextlib.asynccontextmanager
- async def get_order_abn_pdf(
- self, order_id: str, *, request_options: typing.Optional[RequestOptions] = None
- ) -> typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[bytes]]]:
- """
- GET ABN pdf for an order
-
- Parameters
- ----------
- order_id : str
- Your Order ID.
-
- request_options : typing.Optional[RequestOptions]
- Request-specific configuration. You can pass in configuration such as `chunk_size`, and more to customize the request and response.
-
- Returns
- -------
- typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[bytes]]]
- PDF with ABN form
- """
- async with self._client_wrapper.httpx_client.stream(
- f"v3/order/{encode_path_param(order_id)}/abn_pdf",
- method="GET",
- request_options=request_options,
- ) as _response:
-
- async def _stream() -> AsyncHttpResponse[typing.AsyncIterator[bytes]]:
- try:
- if 200 <= _response.status_code < 300:
- _chunk_size = request_options.get("chunk_size", None) if request_options is not None else None
- return AsyncHttpResponse(
- response=_response,
- data=(_chunk async for _chunk in _response.aiter_bytes(chunk_size=_chunk_size)),
- )
- await _response.aread()
- if _response.status_code == 422:
- raise UnprocessableEntityError(
- headers=dict(_response.headers),
- body=typing.cast(
- HttpValidationError,
- parse_obj_as(
- type_=HttpValidationError, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
- _response_json = _response.json()
- except JSONDecodeError:
- raise ApiError(
- status_code=_response.status_code, headers=dict(_response.headers), body=_response.text
- )
- except ValidationError as e:
- raise ParsingError(
- status_code=_response.status_code,
- headers=dict(_response.headers),
- body=_response.json(),
- cause=e,
- )
- raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
-
- yield await _stream()
-
- async def get_order(
- self, order_id: str, *, request_options: typing.Optional[RequestOptions] = None
- ) -> AsyncHttpResponse[ClientFacingOrder]:
+ async def list_unmatched_result_test_cases(
+ self, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> AsyncHttpResponse[ListUnmatchedResultTestCasesResponse]:
"""
- GET individual order by ID.
-
Parameters
----------
- order_id : str
- Your Order ID.
-
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- AsyncHttpResponse[ClientFacingOrder]
+ AsyncHttpResponse[ListUnmatchedResultTestCasesResponse]
Successful Response
"""
_response = await self._client_wrapper.httpx_client.request(
- f"v3/order/{encode_path_param(order_id)}",
+ "v3/unmatched_result_test/case",
method="GET",
request_options=request_options,
)
try:
if 200 <= _response.status_code < 300:
_data = typing.cast(
- ClientFacingOrder,
+ ListUnmatchedResultTestCasesResponse,
parse_obj_as(
- type_=ClientFacingOrder, # type: ignore
+ type_=ListUnmatchedResultTestCasesResponse, # type: ignore
object_=_response.json(),
),
)
return AsyncHttpResponse(response=_response, data=_data)
- if _response.status_code == 422:
- raise UnprocessableEntityError(
- headers=dict(_response.headers),
- body=typing.cast(
- HttpValidationError,
- parse_obj_as(
- type_=HttpValidationError, # type: ignore
- object_=_response.json(),
- ),
- ),
- )
_response_json = _response.json()
except JSONDecodeError:
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
@@ -5853,50 +6895,65 @@ async def get_order(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- async def update_order(
+ async def create_unmatched_result_test(
self,
- order_id: str,
*,
- activate_by: typing.Optional[str] = OMIT,
+ idempotency_key: str,
+ case: UnmatchedResultTestCase,
+ order_source: UnmatchedResultTestOrderSource,
+ orders: typing.Optional[typing.Dict[str, typing.Optional[str]]] = OMIT,
+ result_status: typing.Optional[ResultStatus] = OMIT,
+ interpretation: typing.Optional[Interpretation] = OMIT,
+ lab_test_id: typing.Optional[str] = OMIT,
+ wrong_lab_test_id: typing.Optional[str] = OMIT,
request_options: typing.Optional[RequestOptions] = None,
- ) -> AsyncHttpResponse[PostOrderResponse]:
+ ) -> AsyncHttpResponse[CreateUnmatchedResultTestResponse]:
"""
- Update a modifiable order's scheduled activation date.
+ Parameters
+ ----------
+ idempotency_key : str
+
+ case : UnmatchedResultTestCase
+ ℹ️ This enum is non-exhaustive.
+
+ order_source : UnmatchedResultTestOrderSource
+ ℹ️ This enum is non-exhaustive.
- The order must be in `ordered` or `awaiting_registration` status. Setting
- `activate_by` to a future date reschedules dispatch; setting it to `null`
- clears the schedule and enqueues immediate dispatch for `ordered` orders.
+ orders : typing.Optional[typing.Dict[str, typing.Optional[str]]]
- Returns 400 when:
- - the order is not in a modifiable status,
- - the order was created for immediate processing (cannot be scheduled
- after the fact),
- - `activate_by` is in the past.
+ result_status : typing.Optional[ResultStatus]
+ ℹ️ This enum is non-exhaustive.
- Parameters
- ----------
- order_id : str
- Your Order ID.
+ interpretation : typing.Optional[Interpretation]
+ ℹ️ This enum is non-exhaustive.
- activate_by : typing.Optional[str]
- The date on which the order should be activated (dispatched to the lab). Must be today or a future date. Set to `null` to clear an existing scheduled date and dispatch the order immediately. Note: an order originally created for immediate processing (no `activate_by` at creation time) cannot be rescheduled — only orders that were created with an `activate_by` can have it changed or cleared.
+ lab_test_id : typing.Optional[str]
+
+ wrong_lab_test_id : typing.Optional[str]
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- AsyncHttpResponse[PostOrderResponse]
+ AsyncHttpResponse[CreateUnmatchedResultTestResponse]
Successful Response
"""
_response = await self._client_wrapper.httpx_client.request(
- f"v3/order/{encode_path_param(order_id)}",
- method="PATCH",
+ "v3/unmatched_result_test",
+ method="POST",
json={
- "activate_by": activate_by,
+ "case": case,
+ "order_source": order_source,
+ "orders": orders,
+ "result_status": result_status,
+ "interpretation": interpretation,
+ "lab_test_id": lab_test_id,
+ "wrong_lab_test_id": wrong_lab_test_id,
},
headers={
"content-type": "application/json",
+ "X-Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None,
},
request_options=request_options,
omit=OMIT,
@@ -5904,9 +6961,9 @@ async def update_order(
try:
if 200 <= _response.status_code < 300:
_data = typing.cast(
- PostOrderResponse,
+ CreateUnmatchedResultTestResponse,
parse_obj_as(
- type_=PostOrderResponse, # type: ignore
+ type_=CreateUnmatchedResultTestResponse, # type: ignore
object_=_response.json(),
),
)
@@ -5931,123 +6988,33 @@ async def update_order(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- async def create_order(
- self,
- *,
- user_id: str,
- patient_details: PatientDetailsWithValidation,
- patient_address: PatientAddressWithValidation,
- idempotency_key: typing.Optional[str] = None,
- idempotency_error: typing.Optional[typing.Literal["no-cache"]] = None,
- lab_test_id: typing.Optional[str] = OMIT,
- order_set: typing.Optional[OrderSetRequest] = OMIT,
- collection_method: typing.Optional[LabTestCollectionMethod] = OMIT,
- physician: typing.Optional[PhysicianCreateRequest] = OMIT,
- health_insurance: typing.Optional[HealthInsuranceCreateRequest] = OMIT,
- priority: typing.Optional[bool] = OMIT,
- billing_type: typing.Optional[Billing] = OMIT,
- icd_codes: typing.Optional[typing.Sequence[str]] = OMIT,
- consents: typing.Optional[typing.Sequence[Consent]] = OMIT,
- activate_by: typing.Optional[str] = OMIT,
- aoe_answers: typing.Optional[typing.Sequence[AoEAnswer]] = OMIT,
- passthrough: typing.Optional[str] = OMIT,
- clinical_notes: typing.Optional[str] = OMIT,
- lab_account_id: typing.Optional[str] = OMIT,
- creator_member_id: typing.Optional[str] = OMIT,
- request_options: typing.Optional[RequestOptions] = None,
- ) -> AsyncHttpResponse[PostOrderResponse]:
+ async def get_unmatched_result_test(
+ self, run_id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> AsyncHttpResponse[GetUnmatchedResultTestResponse]:
"""
Parameters
----------
- user_id : str
-
- patient_details : PatientDetailsWithValidation
-
- patient_address : PatientAddressWithValidation
-
- idempotency_key : typing.Optional[str]
-
- idempotency_error : typing.Optional[typing.Literal["no-cache"]]
-
- lab_test_id : typing.Optional[str]
-
- order_set : typing.Optional[OrderSetRequest]
-
- collection_method : typing.Optional[LabTestCollectionMethod]
- ℹ️ This enum is non-exhaustive.
-
- physician : typing.Optional[PhysicianCreateRequest]
-
- health_insurance : typing.Optional[HealthInsuranceCreateRequest]
-
- priority : typing.Optional[bool]
- Defines whether order is priority or not. For some labs, this refers to a STAT order.
-
- billing_type : typing.Optional[Billing]
- ℹ️ This enum is non-exhaustive.
-
- icd_codes : typing.Optional[typing.Sequence[str]]
-
- consents : typing.Optional[typing.Sequence[Consent]]
-
- activate_by : typing.Optional[str]
- Schedule an Order to be processed in a future date.
-
- aoe_answers : typing.Optional[typing.Sequence[AoEAnswer]]
-
- passthrough : typing.Optional[str]
-
- clinical_notes : typing.Optional[str]
-
- lab_account_id : typing.Optional[str]
-
- creator_member_id : typing.Optional[str]
+ run_id : str
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- AsyncHttpResponse[PostOrderResponse]
+ AsyncHttpResponse[GetUnmatchedResultTestResponse]
Successful Response
"""
_response = await self._client_wrapper.httpx_client.request(
- "v3/order",
- method="POST",
- json={
- "user_id": user_id,
- "lab_test_id": lab_test_id,
- "order_set": order_set,
- "collection_method": collection_method,
- "physician": physician,
- "health_insurance": health_insurance,
- "priority": priority,
- "billing_type": billing_type,
- "icd_codes": icd_codes,
- "consents": consents,
- "activate_by": activate_by,
- "aoe_answers": aoe_answers,
- "passthrough": passthrough,
- "clinical_notes": clinical_notes,
- "lab_account_id": lab_account_id,
- "creator_member_id": creator_member_id,
- "patient_details": patient_details,
- "patient_address": patient_address,
- },
- headers={
- "content-type": "application/json",
- "X-Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None,
- "X-Idempotency-Error": str(idempotency_error) if idempotency_error is not None else None,
- },
+ f"v3/unmatched_result_test/{encode_path_param(run_id)}",
+ method="GET",
request_options=request_options,
- omit=OMIT,
)
try:
if 200 <= _response.status_code < 300:
_data = typing.cast(
- PostOrderResponse,
+ GetUnmatchedResultTestResponse,
parse_obj_as(
- type_=PostOrderResponse, # type: ignore
+ type_=GetUnmatchedResultTestResponse, # type: ignore
object_=_response.json(),
),
)
@@ -6072,77 +7039,74 @@ async def create_order(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- async def import_order(
+ async def list_unmatched_results(
self,
*,
- user_id: str,
- billing_type: Billing,
- order_set: OrderSetRequest,
- collection_method: LabTestCollectionMethod,
- patient_details: PatientDetailsWithValidation,
- patient_address: PatientAddress,
- sample_id: str,
- physician: typing.Optional[PhysicianCreateRequest] = OMIT,
- lab_account_id: typing.Optional[str] = OMIT,
+ limit: typing.Optional[int] = None,
+ next_cursor: typing.Optional[str] = None,
+ decision_code: typing.Optional[MatchDecisionCode] = None,
+ lab_slug: typing.Optional[str] = None,
+ status: typing.Optional[MatchReviewStatusFilter] = None,
+ created_at_start: typing.Optional[str] = None,
+ created_at_end: typing.Optional[str] = None,
+ search_input: typing.Optional[str] = None,
request_options: typing.Optional[RequestOptions] = None,
- ) -> AsyncHttpResponse[PostOrderResponse]:
+ ) -> AsyncHttpResponse[ListUnmatchedResultResponse]:
"""
Parameters
----------
- user_id : str
+ limit : typing.Optional[int]
- billing_type : Billing
- ℹ️ This enum is non-exhaustive.
-
- order_set : OrderSetRequest
+ next_cursor : typing.Optional[str]
+ The cursor for fetching the next page, or `null` to fetch the first page.
- collection_method : LabTestCollectionMethod
- ℹ️ This enum is non-exhaustive.
+ decision_code : typing.Optional[MatchDecisionCode]
+ Filter by match decision code.
- patient_details : PatientDetailsWithValidation
+ lab_slug : typing.Optional[str]
+ Filter by lab slug (e.g. `labcorp`, `quest`).
- patient_address : PatientAddress
+ status : typing.Optional[MatchReviewStatusFilter]
+ Filter by review status. `pending_customer_review` returns items awaiting your action; `pending_ops_review` returns items you have escalated for review.
- sample_id : str
+ created_at_start : typing.Optional[str]
+ Filter by result receipt date on or after this date (UTC, inclusive, YYYY-MM-DD).
- physician : typing.Optional[PhysicianCreateRequest]
+ created_at_end : typing.Optional[str]
+ Filter by result receipt date on or before this date (UTC, inclusive, YYYY-MM-DD).
- lab_account_id : typing.Optional[str]
+ search_input : typing.Optional[str]
+ Search by patient first name, last name, or date of birth (e.g. `Alice`, `Smith`, or `1990-01-15`).
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- AsyncHttpResponse[PostOrderResponse]
+ AsyncHttpResponse[ListUnmatchedResultResponse]
Successful Response
"""
_response = await self._client_wrapper.httpx_client.request(
- "v3/order/import",
- method="POST",
- json={
- "user_id": user_id,
- "billing_type": billing_type,
- "order_set": order_set,
- "collection_method": collection_method,
- "physician": physician,
- "patient_details": patient_details,
- "patient_address": patient_address,
- "sample_id": sample_id,
- "lab_account_id": lab_account_id,
- },
- headers={
- "content-type": "application/json",
+ "v3/unmatched_result",
+ method="GET",
+ params={
+ "limit": limit,
+ "next_cursor": next_cursor,
+ "decision_code": decision_code,
+ "lab_slug": lab_slug,
+ "status": status,
+ "created_at_start": created_at_start,
+ "created_at_end": created_at_end,
+ "search_input": search_input,
},
request_options=request_options,
- omit=OMIT,
)
try:
if 200 <= _response.status_code < 300:
_data = typing.cast(
- PostOrderResponse,
+ ListUnmatchedResultResponse,
parse_obj_as(
- type_=PostOrderResponse, # type: ignore
+ type_=ListUnmatchedResultResponse, # type: ignore
object_=_response.json(),
),
)
@@ -6167,36 +7131,33 @@ async def import_order(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- async def cancel_order(
- self, order_id: str, *, request_options: typing.Optional[RequestOptions] = None
- ) -> AsyncHttpResponse[PostOrderResponse]:
+ async def get_unmatched_result(
+ self, raw_result_id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> AsyncHttpResponse[GetUnmatchedResultResponse]:
"""
- POST cancel order
-
Parameters
----------
- order_id : str
- Your Order ID.
+ raw_result_id : str
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- AsyncHttpResponse[PostOrderResponse]
+ AsyncHttpResponse[GetUnmatchedResultResponse]
Successful Response
"""
_response = await self._client_wrapper.httpx_client.request(
- f"v3/order/{encode_path_param(order_id)}/cancel",
- method="POST",
+ f"v3/unmatched_result/{encode_path_param(raw_result_id)}",
+ method="GET",
request_options=request_options,
)
try:
if 200 <= _response.status_code < 300:
_data = typing.cast(
- PostOrderResponse,
+ GetUnmatchedResultResponse,
parse_obj_as(
- type_=PostOrderResponse, # type: ignore
+ type_=GetUnmatchedResultResponse, # type: ignore
object_=_response.json(),
),
)
@@ -6221,44 +7182,42 @@ async def cancel_order(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- async def simulate_order_process(
+ async def accept_unmatched_result(
self,
- order_id: str,
+ raw_result_id: str,
*,
- final_status: typing.Optional[OrderStatus] = None,
- delay: typing.Optional[int] = None,
- request: typing.Optional[SimulationFlags] = None,
+ user_id: typing.Optional[str] = OMIT,
+ order_id: typing.Optional[str] = OMIT,
+ note: typing.Optional[str] = OMIT,
request_options: typing.Optional[RequestOptions] = None,
- ) -> AsyncHttpResponse[typing.Any]:
+ ) -> AsyncHttpResponse[ClientFacingOrder]:
"""
- Get available test kits.
-
Parameters
----------
- order_id : str
+ raw_result_id : str
- final_status : typing.Optional[OrderStatus]
+ user_id : typing.Optional[str]
- delay : typing.Optional[int]
+ order_id : typing.Optional[str]
- request : typing.Optional[SimulationFlags]
+ note : typing.Optional[str]
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- AsyncHttpResponse[typing.Any]
+ AsyncHttpResponse[ClientFacingOrder]
Successful Response
"""
_response = await self._client_wrapper.httpx_client.request(
- f"v3/order/{encode_path_param(order_id)}/test",
+ f"v3/unmatched_result/{encode_path_param(raw_result_id)}/accept",
method="POST",
- params={
- "final_status": final_status,
- "delay": delay,
+ json={
+ "user_id": user_id,
+ "order_id": order_id,
+ "note": note,
},
- json=request,
headers={
"content-type": "application/json",
},
@@ -6266,13 +7225,11 @@ async def simulate_order_process(
omit=OMIT,
)
try:
- if _response is None or not _response.text.strip():
- return AsyncHttpResponse(response=_response, data=None)
if 200 <= _response.status_code < 300:
_data = typing.cast(
- typing.Any,
+ ClientFacingOrder,
parse_obj_as(
- type_=typing.Any, # type: ignore
+ type_=ClientFacingOrder, # type: ignore
object_=_response.json(),
),
)
@@ -6297,36 +7254,51 @@ async def simulate_order_process(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
- async def update_on_site_collection_order_draw_completed(
- self, order_id: str, *, request_options: typing.Optional[RequestOptions] = None
- ) -> AsyncHttpResponse[PostOrderResponse]:
+ async def resolve_unmatched_result(
+ self,
+ raw_result_id: str,
+ *,
+ action: UnmatchedResultResolutionAction,
+ note: typing.Optional[str] = OMIT,
+ request_options: typing.Optional[RequestOptions] = None,
+ ) -> AsyncHttpResponse[UnmatchedResult]:
"""
- PATCH update on site collection order when draw is completed
-
Parameters
----------
- order_id : str
- Your Order ID.
+ raw_result_id : str
+
+ action : UnmatchedResultResolutionAction
+ ℹ️ This enum is non-exhaustive.
+
+ note : typing.Optional[str]
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
Returns
-------
- AsyncHttpResponse[PostOrderResponse]
+ AsyncHttpResponse[UnmatchedResult]
Successful Response
"""
_response = await self._client_wrapper.httpx_client.request(
- f"v3/order/{encode_path_param(order_id)}/draw_completed",
- method="PATCH",
+ f"v3/unmatched_result/{encode_path_param(raw_result_id)}/resolve",
+ method="POST",
+ json={
+ "action": action,
+ "note": note,
+ },
+ headers={
+ "content-type": "application/json",
+ },
request_options=request_options,
+ omit=OMIT,
)
try:
if 200 <= _response.status_code < 300:
_data = typing.cast(
- PostOrderResponse,
+ UnmatchedResult,
parse_obj_as(
- type_=PostOrderResponse, # type: ignore
+ type_=UnmatchedResult, # type: ignore
object_=_response.json(),
),
)
diff --git a/src/junction/menstrual_cycle/client.py b/src/junction/menstrual_cycle/client.py
index dd779c1..76fa6d5 100644
--- a/src/junction/menstrual_cycle/client.py
+++ b/src/junction/menstrual_cycle/client.py
@@ -38,11 +38,13 @@ def get(
user_id : str
start_date : str
+ Start date in YYYY-MM-DD
provider : typing.Optional[str]
Provider oura/strava etc
end_date : typing.Optional[str]
+ End date (inclusive) in YYYY-MM-DD
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
@@ -102,11 +104,13 @@ async def get(
user_id : str
start_date : str
+ Start date in YYYY-MM-DD
provider : typing.Optional[str]
Provider oura/strava etc
end_date : typing.Optional[str]
+ End date (inclusive) in YYYY-MM-DD
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
diff --git a/src/junction/menstrual_cycle/raw_client.py b/src/junction/menstrual_cycle/raw_client.py
index 8ee2f8e..00dfc62 100644
--- a/src/junction/menstrual_cycle/raw_client.py
+++ b/src/junction/menstrual_cycle/raw_client.py
@@ -35,11 +35,13 @@ def get(
user_id : str
start_date : str
+ Start date in YYYY-MM-DD
provider : typing.Optional[str]
Provider oura/strava etc
end_date : typing.Optional[str]
+ End date (inclusive) in YYYY-MM-DD
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
@@ -109,11 +111,13 @@ async def get(
user_id : str
start_date : str
+ Start date in YYYY-MM-DD
provider : typing.Optional[str]
Provider oura/strava etc
end_date : typing.Optional[str]
+ End date (inclusive) in YYYY-MM-DD
request_options : typing.Optional[RequestOptions]
Request-specific configuration.
diff --git a/src/junction/sleep_cycle/client.py b/src/junction/sleep_cycle/client.py
index c823e35..5edf9b3 100644
--- a/src/junction/sleep_cycle/client.py
+++ b/src/junction/sleep_cycle/client.py
@@ -40,8 +40,10 @@ def get(
user_id : str
start_date : str
+ Start date in YYYY-MM-DD
end_date : typing.Optional[str]
+ End date (inclusive) in YYYY-MM-DD
provider : typing.Optional[str]
Provider oura/strava etc
@@ -106,8 +108,10 @@ async def get(
user_id : str
start_date : str
+ Start date in YYYY-MM-DD
end_date : typing.Optional[str]
+ End date (inclusive) in YYYY-MM-DD
provider : typing.Optional[str]
Provider oura/strava etc
diff --git a/src/junction/sleep_cycle/raw_client.py b/src/junction/sleep_cycle/raw_client.py
index 4755470..ae090fc 100644
--- a/src/junction/sleep_cycle/raw_client.py
+++ b/src/junction/sleep_cycle/raw_client.py
@@ -37,8 +37,10 @@ def get(
user_id : str
start_date : str
+ Start date in YYYY-MM-DD
end_date : typing.Optional[str]
+ End date (inclusive) in YYYY-MM-DD
provider : typing.Optional[str]
Provider oura/strava etc
@@ -113,8 +115,10 @@ async def get(
user_id : str
start_date : str
+ Start date in YYYY-MM-DD
end_date : typing.Optional[str]
+ End date (inclusive) in YYYY-MM-DD
provider : typing.Optional[str]
Provider oura/strava etc
diff --git a/src/junction/testkit/client.py b/src/junction/testkit/client.py
index c05a14a..a35672b 100644
--- a/src/junction/testkit/client.py
+++ b/src/junction/testkit/client.py
@@ -54,7 +54,7 @@ def register(
patient_address : PatientAddressWithValidation
user_id : typing.Optional[str]
- The user ID of the patient.
+ The user ID of the patient. If it differs from the user currently associated with the unregistered testkit order, the order is rebound to this user at registration time. The user must exist on the same team as the order. If omitted, the order's existing user is kept.
physician : typing.Optional[PhysicianCreateRequestBase]
@@ -215,7 +215,7 @@ async def register(
patient_address : PatientAddressWithValidation
user_id : typing.Optional[str]
- The user ID of the patient.
+ The user ID of the patient. If it differs from the user currently associated with the unregistered testkit order, the order is rebound to this user at registration time. The user must exist on the same team as the order. If omitted, the order's existing user is kept.
physician : typing.Optional[PhysicianCreateRequestBase]
diff --git a/src/junction/testkit/raw_client.py b/src/junction/testkit/raw_client.py
index 070fc14..86cab09 100644
--- a/src/junction/testkit/raw_client.py
+++ b/src/junction/testkit/raw_client.py
@@ -50,7 +50,7 @@ def register(
patient_address : PatientAddressWithValidation
user_id : typing.Optional[str]
- The user ID of the patient.
+ The user ID of the patient. If it differs from the user currently associated with the unregistered testkit order, the order is rebound to this user at registration time. The user must exist on the same team as the order. If omitted, the order's existing user is kept.
physician : typing.Optional[PhysicianCreateRequestBase]
@@ -220,7 +220,7 @@ async def register(
patient_address : PatientAddressWithValidation
user_id : typing.Optional[str]
- The user ID of the patient.
+ The user ID of the patient. If it differs from the user currently associated with the unregistered testkit order, the order is rebound to this user at registration time. The user must exist on the same team as the order. If omitted, the order's existing user is kept.
physician : typing.Optional[PhysicianCreateRequestBase]
diff --git a/src/junction/types/__init__.py b/src/junction/types/__init__.py
index b3c3182..7b0c100 100644
--- a/src/junction/types/__init__.py
+++ b/src/junction/types/__init__.py
@@ -286,6 +286,8 @@
from .client_facing_loinc import ClientFacingLoinc
from .client_facing_marker import ClientFacingMarker
from .client_facing_marker_complete import ClientFacingMarkerComplete
+ from .client_facing_match_review_changed import ClientFacingMatchReviewChanged
+ from .client_facing_match_review_updated import ClientFacingMatchReviewUpdated
from .client_facing_meal_changed import ClientFacingMealChanged
from .client_facing_meal_changed_event_type import ClientFacingMealChangedEventType
from .client_facing_meal_historical_pull_completed import ClientFacingMealHistoricalPullCompleted
@@ -495,6 +497,7 @@
from .contraceptive_entry import ContraceptiveEntry
from .contraceptive_entry_type import ContraceptiveEntryType
from .convert_compendium_response import ConvertCompendiumResponse
+ from .create_unmatched_result_test_response import CreateUnmatchedResultTestResponse
from .create_user_portal_url_response import CreateUserPortalUrlResponse
from .date_part_expr import DatePartExpr
from .date_part_expr_arg import DatePartExprArg
@@ -525,11 +528,14 @@
from .fats import Fats
from .gender import Gender
from .gender_identity import GenderIdentity
+ from .get_lab_test_pricing_response import GetLabTestPricingResponse
from .get_markers_response import GetMarkersResponse
from .get_order_communication_settings_response import GetOrderCommunicationSettingsResponse
from .get_order_transaction_response import GetOrderTransactionResponse
from .get_orders_response import GetOrdersResponse
from .get_team_lab_accounts_response import GetTeamLabAccountsResponse
+ from .get_unmatched_result_response import GetUnmatchedResultResponse
+ from .get_unmatched_result_test_response import GetUnmatchedResultTestResponse
from .group_key_column_expr import GroupKeyColumnExpr
from .group_key_column_expr_group_key import GroupKeyColumnExprGroupKey
from .grouped_a_fib_burden import GroupedAFibBurden
@@ -678,6 +684,9 @@
from .lab_results_raw_results import LabResultsRawResults
from .lab_test_collection_method import LabTestCollectionMethod
from .lab_test_generation_method_filter import LabTestGenerationMethodFilter
+ from .lab_test_panel_pricing import LabTestPanelPricing
+ from .lab_test_panel_pricing_marker_breakdown_value import LabTestPanelPricingMarkerBreakdownValue
+ from .lab_test_panel_pricing_pricing import LabTestPanelPricingPricing
from .lab_test_resources_response import LabTestResourcesResponse
from .lab_test_sample_type import LabTestSampleType
from .lab_test_status import LabTestStatus
@@ -685,10 +694,29 @@
from .last_attempt import LastAttempt
from .libre_config import LibreConfig
from .link_token_exchange_response import LinkTokenExchangeResponse
+ from .list_unmatched_result_response import ListUnmatchedResultResponse
+ from .list_unmatched_result_test_cases_response import ListUnmatchedResultTestCasesResponse
from .lng_lat import LngLat
from .loinc_match import LoincMatch
from .macros import Macros
+ from .marker_pricing_conditions import MarkerPricingConditions
+ from .marker_pricing_response import MarkerPricingResponse
+ from .marker_pricing_response_data_value import MarkerPricingResponseDataValue
from .marker_type import MarkerType
+ from .match_candidate_confidence_level import MatchCandidateConfidenceLevel
+ from .match_candidate_reason_code import MatchCandidateReasonCode
+ from .match_candidate_type import MatchCandidateType
+ from .match_decision_code import MatchDecisionCode
+ from .match_review_candidate import MatchReviewCandidate
+ from .match_review_candidate_group import MatchReviewCandidateGroup
+ from .match_review_lab import MatchReviewLab
+ from .match_review_marker import MatchReviewMarker
+ from .match_review_patient import MatchReviewPatient
+ from .match_review_resolution_action import MatchReviewResolutionAction
+ from .match_review_status import MatchReviewStatus
+ from .match_review_status_filter import MatchReviewStatusFilter
+ from .match_review_webhook_payload import MatchReviewWebhookPayload
+ from .match_sub_reason_code import MatchSubReasonCode
from .mc_basal_body_temperature_field_expr import McBasalBodyTemperatureFieldExpr
from .mc_basal_body_temperature_field_expr_basal_body_temperature import (
McBasalBodyTemperatureFieldExprBasalBodyTemperature,
@@ -765,6 +793,11 @@
from .placeholder import Placeholder
from .png import Png
from .post_order_response import PostOrderResponse
+ from .pricing_modifier_marker_pricing_conditions import PricingModifierMarkerPricingConditions
+ from .pricing_modifier_marker_pricing_conditions_delta_amount_minor import (
+ PricingModifierMarkerPricingConditionsDeltaAmountMinor,
+ )
+ from .pricing_modifier_range import PricingModifierRange
from .priority_resource import PriorityResource
from .profile_column_expr import ProfileColumnExpr
from .profile_column_expr_profile import ProfileColumnExprProfile
@@ -801,6 +834,7 @@
from .raw_sleep_response import RawSleepResponse
from .raw_workout import RawWorkout
from .raw_workout_response import RawWorkoutResponse
+ from .reflex_pricing_condition import ReflexPricingCondition
from .region import Region
from .related_candidate import RelatedCandidate
from .relative_timeframe import RelativeTimeframe
@@ -809,6 +843,7 @@
from .responsible_relationship import ResponsibleRelationship
from .result_metadata import ResultMetadata
from .result_metadata_gender import ResultMetadataGender
+ from .result_status import ResultStatus
from .result_type import ResultType
from .sample_data import SampleData
from .sample_data_date_collected import SampleDataDateCollected
@@ -821,6 +856,7 @@
from .scope_requirements_str import ScopeRequirementsStr
from .search_compendium_response import SearchCompendiumResponse
from .search_mode import SearchMode
+ from .search_orderable_tests_response import SearchOrderableTestsResponse
from .select import Select
from .sex import Sex
from .sexual_activity_entry import SexualActivityEntry
@@ -845,6 +881,7 @@
from .source_column_expr_source import SourceColumnExprSource
from .source_link import SourceLink
from .source_type import SourceType
+ from .specified_pricing_marker_pricing_conditions import SpecifiedPricingMarkerPricingConditions
from .team_config import TeamConfig
from .temperature_timeseries_expr import TemperatureTimeseriesExpr
from .temperature_timeseries_expr_field import TemperatureTimeseriesExprField
@@ -853,11 +890,23 @@
from .timeseries_metric_point import TimeseriesMetricPoint
from .timeseries_resource import TimeseriesResource
from .trace_elements import TraceElements
+ from .unmatched_result import UnmatchedResult
+ from .unmatched_result_resolution_action import UnmatchedResultResolutionAction
+ from .unmatched_result_test_case import UnmatchedResultTestCase
+ from .unmatched_result_test_case_definition import UnmatchedResultTestCaseDefinition
+ from .unmatched_result_test_failure import UnmatchedResultTestFailure
+ from .unmatched_result_test_order_artifact import UnmatchedResultTestOrderArtifact
+ from .unmatched_result_test_order_source import UnmatchedResultTestOrderSource
+ from .unmatched_result_test_run_status import UnmatchedResultTestRunStatus
+ from .unmatched_result_test_stage import UnmatchedResultTestStage
from .unnest_expr import UnnestExpr
from .unnest_expr_unnest import UnnestExprUnnest
from .unrecognized_value_macro_expr import UnrecognizedValueMacroExpr
+ from .unspecified_pricing import UnspecifiedPricing
+ from .unspecified_pricing_unspecified import UnspecifiedPricingUnspecified
from .us_address import UsAddress
from .us_state import UsState
+ from .us_state_pricing_condition import UsStatePricingCondition
from .user_address import UserAddress
from .user_historical_pulls_response import UserHistoricalPullsResponse
from .user_info import UserInfo
@@ -1124,6 +1173,8 @@
"ClientFacingLoinc": ".client_facing_loinc",
"ClientFacingMarker": ".client_facing_marker",
"ClientFacingMarkerComplete": ".client_facing_marker_complete",
+ "ClientFacingMatchReviewChanged": ".client_facing_match_review_changed",
+ "ClientFacingMatchReviewUpdated": ".client_facing_match_review_updated",
"ClientFacingMealChanged": ".client_facing_meal_changed",
"ClientFacingMealChangedEventType": ".client_facing_meal_changed_event_type",
"ClientFacingMealHistoricalPullCompleted": ".client_facing_meal_historical_pull_completed",
@@ -1303,6 +1354,7 @@
"ContraceptiveEntry": ".contraceptive_entry",
"ContraceptiveEntryType": ".contraceptive_entry_type",
"ConvertCompendiumResponse": ".convert_compendium_response",
+ "CreateUnmatchedResultTestResponse": ".create_unmatched_result_test_response",
"CreateUserPortalUrlResponse": ".create_user_portal_url_response",
"DatePartExpr": ".date_part_expr",
"DatePartExprArg": ".date_part_expr_arg",
@@ -1333,11 +1385,14 @@
"Fats": ".fats",
"Gender": ".gender",
"GenderIdentity": ".gender_identity",
+ "GetLabTestPricingResponse": ".get_lab_test_pricing_response",
"GetMarkersResponse": ".get_markers_response",
"GetOrderCommunicationSettingsResponse": ".get_order_communication_settings_response",
"GetOrderTransactionResponse": ".get_order_transaction_response",
"GetOrdersResponse": ".get_orders_response",
"GetTeamLabAccountsResponse": ".get_team_lab_accounts_response",
+ "GetUnmatchedResultResponse": ".get_unmatched_result_response",
+ "GetUnmatchedResultTestResponse": ".get_unmatched_result_test_response",
"GroupKeyColumnExpr": ".group_key_column_expr",
"GroupKeyColumnExprGroupKey": ".group_key_column_expr_group_key",
"GroupedAFibBurden": ".grouped_a_fib_burden",
@@ -1484,6 +1539,9 @@
"LabResultsRawResults": ".lab_results_raw_results",
"LabTestCollectionMethod": ".lab_test_collection_method",
"LabTestGenerationMethodFilter": ".lab_test_generation_method_filter",
+ "LabTestPanelPricing": ".lab_test_panel_pricing",
+ "LabTestPanelPricingMarkerBreakdownValue": ".lab_test_panel_pricing_marker_breakdown_value",
+ "LabTestPanelPricingPricing": ".lab_test_panel_pricing_pricing",
"LabTestResourcesResponse": ".lab_test_resources_response",
"LabTestSampleType": ".lab_test_sample_type",
"LabTestStatus": ".lab_test_status",
@@ -1491,10 +1549,29 @@
"LastAttempt": ".last_attempt",
"LibreConfig": ".libre_config",
"LinkTokenExchangeResponse": ".link_token_exchange_response",
+ "ListUnmatchedResultResponse": ".list_unmatched_result_response",
+ "ListUnmatchedResultTestCasesResponse": ".list_unmatched_result_test_cases_response",
"LngLat": ".lng_lat",
"LoincMatch": ".loinc_match",
"Macros": ".macros",
+ "MarkerPricingConditions": ".marker_pricing_conditions",
+ "MarkerPricingResponse": ".marker_pricing_response",
+ "MarkerPricingResponseDataValue": ".marker_pricing_response_data_value",
"MarkerType": ".marker_type",
+ "MatchCandidateConfidenceLevel": ".match_candidate_confidence_level",
+ "MatchCandidateReasonCode": ".match_candidate_reason_code",
+ "MatchCandidateType": ".match_candidate_type",
+ "MatchDecisionCode": ".match_decision_code",
+ "MatchReviewCandidate": ".match_review_candidate",
+ "MatchReviewCandidateGroup": ".match_review_candidate_group",
+ "MatchReviewLab": ".match_review_lab",
+ "MatchReviewMarker": ".match_review_marker",
+ "MatchReviewPatient": ".match_review_patient",
+ "MatchReviewResolutionAction": ".match_review_resolution_action",
+ "MatchReviewStatus": ".match_review_status",
+ "MatchReviewStatusFilter": ".match_review_status_filter",
+ "MatchReviewWebhookPayload": ".match_review_webhook_payload",
+ "MatchSubReasonCode": ".match_sub_reason_code",
"McBasalBodyTemperatureFieldExpr": ".mc_basal_body_temperature_field_expr",
"McBasalBodyTemperatureFieldExprBasalBodyTemperature": ".mc_basal_body_temperature_field_expr_basal_body_temperature",
"McCervicalMucusFieldExpr": ".mc_cervical_mucus_field_expr",
@@ -1567,6 +1644,9 @@
"Placeholder": ".placeholder",
"Png": ".png",
"PostOrderResponse": ".post_order_response",
+ "PricingModifierMarkerPricingConditions": ".pricing_modifier_marker_pricing_conditions",
+ "PricingModifierMarkerPricingConditionsDeltaAmountMinor": ".pricing_modifier_marker_pricing_conditions_delta_amount_minor",
+ "PricingModifierRange": ".pricing_modifier_range",
"PriorityResource": ".priority_resource",
"ProfileColumnExpr": ".profile_column_expr",
"ProfileColumnExprProfile": ".profile_column_expr_profile",
@@ -1603,6 +1683,7 @@
"RawSleepResponse": ".raw_sleep_response",
"RawWorkout": ".raw_workout",
"RawWorkoutResponse": ".raw_workout_response",
+ "ReflexPricingCondition": ".reflex_pricing_condition",
"Region": ".region",
"RelatedCandidate": ".related_candidate",
"RelativeTimeframe": ".relative_timeframe",
@@ -1611,6 +1692,7 @@
"ResponsibleRelationship": ".responsible_relationship",
"ResultMetadata": ".result_metadata",
"ResultMetadataGender": ".result_metadata_gender",
+ "ResultStatus": ".result_status",
"ResultType": ".result_type",
"SampleData": ".sample_data",
"SampleDataDateCollected": ".sample_data_date_collected",
@@ -1623,6 +1705,7 @@
"ScopeRequirementsStr": ".scope_requirements_str",
"SearchCompendiumResponse": ".search_compendium_response",
"SearchMode": ".search_mode",
+ "SearchOrderableTestsResponse": ".search_orderable_tests_response",
"Select": ".select",
"Sex": ".sex",
"SexualActivityEntry": ".sexual_activity_entry",
@@ -1647,6 +1730,7 @@
"SourceColumnExprSource": ".source_column_expr_source",
"SourceLink": ".source_link",
"SourceType": ".source_type",
+ "SpecifiedPricingMarkerPricingConditions": ".specified_pricing_marker_pricing_conditions",
"TeamConfig": ".team_config",
"TemperatureTimeseriesExpr": ".temperature_timeseries_expr",
"TemperatureTimeseriesExprField": ".temperature_timeseries_expr_field",
@@ -1655,11 +1739,23 @@
"TimeseriesMetricPoint": ".timeseries_metric_point",
"TimeseriesResource": ".timeseries_resource",
"TraceElements": ".trace_elements",
+ "UnmatchedResult": ".unmatched_result",
+ "UnmatchedResultResolutionAction": ".unmatched_result_resolution_action",
+ "UnmatchedResultTestCase": ".unmatched_result_test_case",
+ "UnmatchedResultTestCaseDefinition": ".unmatched_result_test_case_definition",
+ "UnmatchedResultTestFailure": ".unmatched_result_test_failure",
+ "UnmatchedResultTestOrderArtifact": ".unmatched_result_test_order_artifact",
+ "UnmatchedResultTestOrderSource": ".unmatched_result_test_order_source",
+ "UnmatchedResultTestRunStatus": ".unmatched_result_test_run_status",
+ "UnmatchedResultTestStage": ".unmatched_result_test_stage",
"UnnestExpr": ".unnest_expr",
"UnnestExprUnnest": ".unnest_expr_unnest",
"UnrecognizedValueMacroExpr": ".unrecognized_value_macro_expr",
+ "UnspecifiedPricing": ".unspecified_pricing",
+ "UnspecifiedPricingUnspecified": ".unspecified_pricing_unspecified",
"UsAddress": ".us_address",
"UsState": ".us_state",
+ "UsStatePricingCondition": ".us_state_pricing_condition",
"UserAddress": ".user_address",
"UserHistoricalPullsResponse": ".user_historical_pulls_response",
"UserInfo": ".user_info",
@@ -1946,6 +2042,8 @@ def __dir__():
"ClientFacingLoinc",
"ClientFacingMarker",
"ClientFacingMarkerComplete",
+ "ClientFacingMatchReviewChanged",
+ "ClientFacingMatchReviewUpdated",
"ClientFacingMealChanged",
"ClientFacingMealChangedEventType",
"ClientFacingMealHistoricalPullCompleted",
@@ -2125,6 +2223,7 @@ def __dir__():
"ContraceptiveEntry",
"ContraceptiveEntryType",
"ConvertCompendiumResponse",
+ "CreateUnmatchedResultTestResponse",
"CreateUserPortalUrlResponse",
"DatePartExpr",
"DatePartExprArg",
@@ -2155,11 +2254,14 @@ def __dir__():
"Fats",
"Gender",
"GenderIdentity",
+ "GetLabTestPricingResponse",
"GetMarkersResponse",
"GetOrderCommunicationSettingsResponse",
"GetOrderTransactionResponse",
"GetOrdersResponse",
"GetTeamLabAccountsResponse",
+ "GetUnmatchedResultResponse",
+ "GetUnmatchedResultTestResponse",
"GroupKeyColumnExpr",
"GroupKeyColumnExprGroupKey",
"GroupedAFibBurden",
@@ -2306,6 +2408,9 @@ def __dir__():
"LabResultsRawResults",
"LabTestCollectionMethod",
"LabTestGenerationMethodFilter",
+ "LabTestPanelPricing",
+ "LabTestPanelPricingMarkerBreakdownValue",
+ "LabTestPanelPricingPricing",
"LabTestResourcesResponse",
"LabTestSampleType",
"LabTestStatus",
@@ -2313,10 +2418,29 @@ def __dir__():
"LastAttempt",
"LibreConfig",
"LinkTokenExchangeResponse",
+ "ListUnmatchedResultResponse",
+ "ListUnmatchedResultTestCasesResponse",
"LngLat",
"LoincMatch",
"Macros",
+ "MarkerPricingConditions",
+ "MarkerPricingResponse",
+ "MarkerPricingResponseDataValue",
"MarkerType",
+ "MatchCandidateConfidenceLevel",
+ "MatchCandidateReasonCode",
+ "MatchCandidateType",
+ "MatchDecisionCode",
+ "MatchReviewCandidate",
+ "MatchReviewCandidateGroup",
+ "MatchReviewLab",
+ "MatchReviewMarker",
+ "MatchReviewPatient",
+ "MatchReviewResolutionAction",
+ "MatchReviewStatus",
+ "MatchReviewStatusFilter",
+ "MatchReviewWebhookPayload",
+ "MatchSubReasonCode",
"McBasalBodyTemperatureFieldExpr",
"McBasalBodyTemperatureFieldExprBasalBodyTemperature",
"McCervicalMucusFieldExpr",
@@ -2389,6 +2513,9 @@ def __dir__():
"Placeholder",
"Png",
"PostOrderResponse",
+ "PricingModifierMarkerPricingConditions",
+ "PricingModifierMarkerPricingConditionsDeltaAmountMinor",
+ "PricingModifierRange",
"PriorityResource",
"ProfileColumnExpr",
"ProfileColumnExprProfile",
@@ -2425,6 +2552,7 @@ def __dir__():
"RawSleepResponse",
"RawWorkout",
"RawWorkoutResponse",
+ "ReflexPricingCondition",
"Region",
"RelatedCandidate",
"RelativeTimeframe",
@@ -2433,6 +2561,7 @@ def __dir__():
"ResponsibleRelationship",
"ResultMetadata",
"ResultMetadataGender",
+ "ResultStatus",
"ResultType",
"SampleData",
"SampleDataDateCollected",
@@ -2445,6 +2574,7 @@ def __dir__():
"ScopeRequirementsStr",
"SearchCompendiumResponse",
"SearchMode",
+ "SearchOrderableTestsResponse",
"Select",
"Sex",
"SexualActivityEntry",
@@ -2469,6 +2599,7 @@ def __dir__():
"SourceColumnExprSource",
"SourceLink",
"SourceType",
+ "SpecifiedPricingMarkerPricingConditions",
"TeamConfig",
"TemperatureTimeseriesExpr",
"TemperatureTimeseriesExprField",
@@ -2477,11 +2608,23 @@ def __dir__():
"TimeseriesMetricPoint",
"TimeseriesResource",
"TraceElements",
+ "UnmatchedResult",
+ "UnmatchedResultResolutionAction",
+ "UnmatchedResultTestCase",
+ "UnmatchedResultTestCaseDefinition",
+ "UnmatchedResultTestFailure",
+ "UnmatchedResultTestOrderArtifact",
+ "UnmatchedResultTestOrderSource",
+ "UnmatchedResultTestRunStatus",
+ "UnmatchedResultTestStage",
"UnnestExpr",
"UnnestExprUnnest",
"UnrecognizedValueMacroExpr",
+ "UnspecifiedPricing",
+ "UnspecifiedPricingUnspecified",
"UsAddress",
"UsState",
+ "UsStatePricingCondition",
"UserAddress",
"UserHistoricalPullsResponse",
"UserInfo",
diff --git a/src/junction/types/biomarker_result.py b/src/junction/types/biomarker_result.py
index 5bdf097..3729c4e 100644
--- a/src/junction/types/biomarker_result.py
+++ b/src/junction/types/biomarker_result.py
@@ -31,6 +31,7 @@ class BiomarkerResult(UniversalBaseModel):
is_above_max_range: typing.Optional[bool] = None
is_below_min_range: typing.Optional[bool] = None
interpretation: typing.Optional[str] = None
+ source_interpretation: typing.Optional[str] = None
loinc: typing.Optional[str] = None
loinc_slug: typing.Optional[str] = None
provider_id: typing.Optional[str] = None
diff --git a/src/junction/types/client_facing_lab.py b/src/junction/types/client_facing_lab.py
index 5cd6f90..4c6fbac 100644
--- a/src/junction/types/client_facing_lab.py
+++ b/src/junction/types/client_facing_lab.py
@@ -17,6 +17,7 @@ class ClientFacingLab(UniversalBaseModel):
zipcode: str
collection_methods: typing.List[LabTestCollectionMethod]
sample_types: typing.List[LabTestSampleType]
+ logo_url: typing.Optional[str] = None
if IS_PYDANTIC_V2:
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
diff --git a/src/junction/types/client_facing_match_review_changed.py b/src/junction/types/client_facing_match_review_changed.py
new file mode 100644
index 0000000..da9beae
--- /dev/null
+++ b/src/junction/types/client_facing_match_review_changed.py
@@ -0,0 +1,22 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel
+from .match_review_webhook_payload import MatchReviewWebhookPayload
+
+
+class ClientFacingMatchReviewChanged(UniversalBaseModel):
+ event_type: typing.Literal["labtest.match_review.created"] = "labtest.match_review.created"
+ team_id: str
+ data: MatchReviewWebhookPayload
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/junction/types/client_facing_match_review_updated.py b/src/junction/types/client_facing_match_review_updated.py
new file mode 100644
index 0000000..6700955
--- /dev/null
+++ b/src/junction/types/client_facing_match_review_updated.py
@@ -0,0 +1,22 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel
+from .match_review_webhook_payload import MatchReviewWebhookPayload
+
+
+class ClientFacingMatchReviewUpdated(UniversalBaseModel):
+ event_type: typing.Literal["labtest.match_review.updated"] = "labtest.match_review.updated"
+ team_id: str
+ data: MatchReviewWebhookPayload
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/junction/types/client_facing_resource.py b/src/junction/types/client_facing_resource.py
index 5d46104..034a346 100644
--- a/src/junction/types/client_facing_resource.py
+++ b/src/junction/types/client_facing_resource.py
@@ -23,6 +23,7 @@ class ClientFacingResource(enum.StrEnum):
RESULT = "result"
MATCH_REVIEW = "match_review"
APPOINTMENT = "appointment"
+ RESULT_TABLE = "result_table"
GLUCOSE = "glucose"
HEARTRATE = "heartrate"
HRV = "hrv"
@@ -105,6 +106,7 @@ def visit(
result: typing.Callable[[], T_Result],
match_review: typing.Callable[[], T_Result],
appointment: typing.Callable[[], T_Result],
+ result_table: typing.Callable[[], T_Result],
glucose: typing.Callable[[], T_Result],
heartrate: typing.Callable[[], T_Result],
hrv: typing.Callable[[], T_Result],
@@ -187,6 +189,8 @@ def visit(
return match_review()
if self is ClientFacingResource.APPOINTMENT:
return appointment()
+ if self is ClientFacingResource.RESULT_TABLE:
+ return result_table()
if self is ClientFacingResource.GLUCOSE:
return glucose()
if self is ClientFacingResource.HEARTRATE:
diff --git a/src/junction/types/client_facing_workout.py b/src/junction/types/client_facing_workout.py
index 7e44fb9..e41b3fb 100644
--- a/src/junction/types/client_facing_workout.py
+++ b/src/junction/types/client_facing_workout.py
@@ -134,6 +134,10 @@ class ClientFacingWorkout(UniversalBaseModel):
FieldMetadata(alias="map"),
pydantic.Field(alias="map", description="Map of the workout"),
] = None
+ """
+ Map of the workout
+ """
+
provider_id: str = pydantic.Field()
"""
Provider ID given for that specific workout
diff --git a/src/junction/types/consent.py b/src/junction/types/consent.py
index a041528..8e5837b 100644
--- a/src/junction/types/consent.py
+++ b/src/junction/types/consent.py
@@ -16,6 +16,10 @@ class Consent(UniversalBaseModel):
FieldMetadata(alias="consentType"),
pydantic.Field(alias="consentType", description="ℹ️ This enum is non-exhaustive."),
]
+ """
+ ℹ️ This enum is non-exhaustive.
+ """
+
version: typing.Optional[str] = None
time_of_consent: typing_extensions.Annotated[
typing.Optional[dt.datetime], FieldMetadata(alias="timeOfConsent"), pydantic.Field(alias="timeOfConsent")
diff --git a/src/junction/types/create_unmatched_result_test_response.py b/src/junction/types/create_unmatched_result_test_response.py
new file mode 100644
index 0000000..ef4632a
--- /dev/null
+++ b/src/junction/types/create_unmatched_result_test_response.py
@@ -0,0 +1,24 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel
+from .unmatched_result_test_run_status import UnmatchedResultTestRunStatus
+
+
+class CreateUnmatchedResultTestResponse(UniversalBaseModel):
+ run_id: str
+ status: UnmatchedResultTestRunStatus = pydantic.Field()
+ """
+ ℹ️ This enum is non-exhaustive.
+ """
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/junction/types/get_lab_test_pricing_response.py b/src/junction/types/get_lab_test_pricing_response.py
new file mode 100644
index 0000000..c2304d6
--- /dev/null
+++ b/src/junction/types/get_lab_test_pricing_response.py
@@ -0,0 +1,25 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel
+from .lab_test_panel_pricing import LabTestPanelPricing
+
+
+class GetLabTestPricingResponse(UniversalBaseModel):
+ """
+ Aggregated per-panel pricing, keyed by lab test ID.
+ """
+
+ currency: typing.Optional[typing.Literal["USD"]] = None
+ data: typing.Dict[str, LabTestPanelPricing]
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/junction/types/get_markers_response.py b/src/junction/types/get_markers_response.py
index 3b13e74..2173af2 100644
--- a/src/junction/types/get_markers_response.py
+++ b/src/junction/types/get_markers_response.py
@@ -5,6 +5,7 @@
import pydantic
from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel
from .client_facing_marker_complete import ClientFacingMarkerComplete
+from .marker_pricing_response import MarkerPricingResponse
class GetMarkersResponse(UniversalBaseModel):
@@ -13,6 +14,7 @@ class GetMarkersResponse(UniversalBaseModel):
page: typing.Optional[int] = None
size: typing.Optional[int] = None
pages: typing.Optional[int] = None
+ pricing: typing.Optional[MarkerPricingResponse] = None
if IS_PYDANTIC_V2:
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
diff --git a/src/junction/types/get_unmatched_result_response.py b/src/junction/types/get_unmatched_result_response.py
new file mode 100644
index 0000000..0a92286
--- /dev/null
+++ b/src/junction/types/get_unmatched_result_response.py
@@ -0,0 +1,68 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import datetime as dt
+import typing
+
+import pydantic
+from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel
+from .interpretation import Interpretation
+from .match_decision_code import MatchDecisionCode
+from .match_review_candidate_group import MatchReviewCandidateGroup
+from .match_review_lab import MatchReviewLab
+from .match_review_marker import MatchReviewMarker
+from .match_review_patient import MatchReviewPatient
+from .match_review_resolution_action import MatchReviewResolutionAction
+from .match_review_status import MatchReviewStatus
+from .match_sub_reason_code import MatchSubReasonCode
+from .result_status import ResultStatus
+
+
+class GetUnmatchedResultResponse(UniversalBaseModel):
+ id: str
+ status: MatchReviewStatus = pydantic.Field()
+ """
+ ℹ️ This enum is non-exhaustive.
+ """
+
+ decision_code: MatchDecisionCode = pydantic.Field()
+ """
+ ℹ️ This enum is non-exhaustive.
+ """
+
+ sub_reason_codes: typing.Optional[typing.List[MatchSubReasonCode]] = None
+ reason: str
+ patient: typing.Optional[MatchReviewPatient] = None
+ lab: MatchReviewLab
+ markers: typing.Optional[typing.List[MatchReviewMarker]] = None
+ interpretation: typing.Optional[Interpretation] = pydantic.Field(default=None)
+ """
+ ℹ️ This enum is non-exhaustive.
+ """
+
+ result_status: typing.Optional[ResultStatus] = pydantic.Field(default=None)
+ """
+ ℹ️ This enum is non-exhaustive.
+ """
+
+ note: typing.Optional[str] = None
+ resolution_action: typing.Optional[MatchReviewResolutionAction] = pydantic.Field(default=None)
+ """
+ ℹ️ This enum is non-exhaustive.
+ """
+
+ resolved_user_id: typing.Optional[str] = None
+ resolved_order_id: typing.Optional[str] = None
+ allowed_actions: typing.Optional[typing.List[MatchReviewResolutionAction]] = None
+ candidate_groups: typing.Optional[typing.List[MatchReviewCandidateGroup]] = None
+ created_at: dt.datetime
+ updated_at: dt.datetime
+ reviewed_at: typing.Optional[dt.datetime] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/junction/types/get_unmatched_result_test_response.py b/src/junction/types/get_unmatched_result_test_response.py
new file mode 100644
index 0000000..f4687ea
--- /dev/null
+++ b/src/junction/types/get_unmatched_result_test_response.py
@@ -0,0 +1,59 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import datetime as dt
+import typing
+
+import pydantic
+from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel
+from .result_status import ResultStatus
+from .unmatched_result_test_case import UnmatchedResultTestCase
+from .unmatched_result_test_failure import UnmatchedResultTestFailure
+from .unmatched_result_test_order_artifact import UnmatchedResultTestOrderArtifact
+from .unmatched_result_test_order_source import UnmatchedResultTestOrderSource
+from .unmatched_result_test_run_status import UnmatchedResultTestRunStatus
+from .unmatched_result_test_stage import UnmatchedResultTestStage
+
+
+class GetUnmatchedResultTestResponse(UniversalBaseModel):
+ run_id: str
+ status: UnmatchedResultTestRunStatus = pydantic.Field()
+ """
+ ℹ️ This enum is non-exhaustive.
+ """
+
+ stage: UnmatchedResultTestStage = pydantic.Field()
+ """
+ ℹ️ This enum is non-exhaustive.
+ """
+
+ case: UnmatchedResultTestCase = pydantic.Field()
+ """
+ ℹ️ This enum is non-exhaustive.
+ """
+
+ order_source: UnmatchedResultTestOrderSource = pydantic.Field()
+ """
+ ℹ️ This enum is non-exhaustive.
+ """
+
+ result_status: ResultStatus = pydantic.Field()
+ """
+ ℹ️ This enum is non-exhaustive.
+ """
+
+ orders: typing.Optional[typing.Dict[str, UnmatchedResultTestOrderArtifact]] = None
+ raw_result_id: typing.Optional[str] = None
+ error: typing.Optional[UnmatchedResultTestFailure] = None
+ created_at: dt.datetime
+ updated_at: dt.datetime
+ started_at: typing.Optional[dt.datetime] = None
+ completed_at: typing.Optional[dt.datetime] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/junction/types/lab_location_metadata.py b/src/junction/types/lab_location_metadata.py
index 5b35890..0156136 100644
--- a/src/junction/types/lab_location_metadata.py
+++ b/src/junction/types/lab_location_metadata.py
@@ -16,6 +16,7 @@ class LabLocationMetadata(UniversalBaseModel):
phone_number: typing.Optional[str] = None
fax_number: typing.Optional[str] = None
hours: typing.Optional[typing.Dict[str, typing.Any]] = None
+ website: typing.Optional[str] = None
if IS_PYDANTIC_V2:
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
diff --git a/src/junction/types/lab_test_panel_pricing.py b/src/junction/types/lab_test_panel_pricing.py
new file mode 100644
index 0000000..4cc44da
--- /dev/null
+++ b/src/junction/types/lab_test_panel_pricing.py
@@ -0,0 +1,22 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel
+from .lab_test_panel_pricing_marker_breakdown_value import LabTestPanelPricingMarkerBreakdownValue
+from .lab_test_panel_pricing_pricing import LabTestPanelPricingPricing
+
+
+class LabTestPanelPricing(UniversalBaseModel):
+ pricing: LabTestPanelPricingPricing
+ marker_breakdown: typing.Optional[typing.Dict[str, typing.Optional[LabTestPanelPricingMarkerBreakdownValue]]] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/junction/types/lab_test_panel_pricing_marker_breakdown_value.py b/src/junction/types/lab_test_panel_pricing_marker_breakdown_value.py
new file mode 100644
index 0000000..910cb7c
--- /dev/null
+++ b/src/junction/types/lab_test_panel_pricing_marker_breakdown_value.py
@@ -0,0 +1,8 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+from .specified_pricing_marker_pricing_conditions import SpecifiedPricingMarkerPricingConditions
+from .unspecified_pricing import UnspecifiedPricing
+
+LabTestPanelPricingMarkerBreakdownValue = typing.Union[SpecifiedPricingMarkerPricingConditions, UnspecifiedPricing]
diff --git a/src/junction/types/lab_test_panel_pricing_pricing.py b/src/junction/types/lab_test_panel_pricing_pricing.py
new file mode 100644
index 0000000..85074e1
--- /dev/null
+++ b/src/junction/types/lab_test_panel_pricing_pricing.py
@@ -0,0 +1,8 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+from .specified_pricing_marker_pricing_conditions import SpecifiedPricingMarkerPricingConditions
+from .unspecified_pricing import UnspecifiedPricing
+
+LabTestPanelPricingPricing = typing.Union[SpecifiedPricingMarkerPricingConditions, UnspecifiedPricing]
diff --git a/src/junction/types/lab_test_resources_response.py b/src/junction/types/lab_test_resources_response.py
index 56d8354..a1ea12c 100644
--- a/src/junction/types/lab_test_resources_response.py
+++ b/src/junction/types/lab_test_resources_response.py
@@ -5,6 +5,7 @@
import pydantic
from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel
from .client_facing_lab_test import ClientFacingLabTest
+from .get_lab_test_pricing_response import GetLabTestPricingResponse
class LabTestResourcesResponse(UniversalBaseModel):
@@ -14,6 +15,8 @@ class LabTestResourcesResponse(UniversalBaseModel):
The cursor for fetching the next page, or `null` to fetch the first page.
"""
+ pricing: typing.Optional[GetLabTestPricingResponse] = None
+
if IS_PYDANTIC_V2:
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
else:
diff --git a/src/junction/types/labs.py b/src/junction/types/labs.py
index 9fb8bf3..97ac47e 100644
--- a/src/junction/types/labs.py
+++ b/src/junction/types/labs.py
@@ -26,6 +26,7 @@ class Labs(enum.StrEnum):
NEXUS = "nexus"
MY_UTI = "my_uti"
CRL = "crl"
+ MTL = "mtl"
_UNKNOWN = "__LABS_UNKNOWN__"
"""
This member is used for forward compatibility. If the value is not recognized by the enum, it will be stored here, and the raw value is accessible through `.value`.
@@ -53,6 +54,7 @@ def visit(
nexus: typing.Callable[[], T_Result],
my_uti: typing.Callable[[], T_Result],
crl: typing.Callable[[], T_Result],
+ mtl: typing.Callable[[], T_Result],
_unknown_member: typing.Callable[[str], T_Result],
) -> T_Result:
if self is Labs.AYUMETRIX:
@@ -83,4 +85,6 @@ def visit(
return my_uti()
if self is Labs.CRL:
return crl()
+ if self is Labs.MTL:
+ return mtl()
return _unknown_member(self._value_)
diff --git a/src/junction/types/list_unmatched_result_response.py b/src/junction/types/list_unmatched_result_response.py
new file mode 100644
index 0000000..ea97a0d
--- /dev/null
+++ b/src/junction/types/list_unmatched_result_response.py
@@ -0,0 +1,22 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel
+from .unmatched_result import UnmatchedResult
+
+
+class ListUnmatchedResultResponse(UniversalBaseModel):
+ data: typing.Optional[typing.List[UnmatchedResult]] = None
+ next_cursor: typing.Optional[str] = None
+ total: int
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/junction/types/list_unmatched_result_test_cases_response.py b/src/junction/types/list_unmatched_result_test_cases_response.py
new file mode 100644
index 0000000..f57c3b6
--- /dev/null
+++ b/src/junction/types/list_unmatched_result_test_cases_response.py
@@ -0,0 +1,20 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel
+from .unmatched_result_test_case_definition import UnmatchedResultTestCaseDefinition
+
+
+class ListUnmatchedResultTestCasesResponse(UniversalBaseModel):
+ data: typing.List[UnmatchedResultTestCaseDefinition]
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/junction/types/marker_pricing_conditions.py b/src/junction/types/marker_pricing_conditions.py
new file mode 100644
index 0000000..ff9eda1
--- /dev/null
+++ b/src/junction/types/marker_pricing_conditions.py
@@ -0,0 +1,22 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel
+from .reflex_pricing_condition import ReflexPricingCondition
+from .us_state_pricing_condition import UsStatePricingCondition
+
+
+class MarkerPricingConditions(UniversalBaseModel):
+ us_state: typing.Optional[UsStatePricingCondition] = None
+ reflex: typing.Optional[ReflexPricingCondition] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/junction/types/marker_pricing_response.py b/src/junction/types/marker_pricing_response.py
new file mode 100644
index 0000000..f2e3f72
--- /dev/null
+++ b/src/junction/types/marker_pricing_response.py
@@ -0,0 +1,25 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel
+from .marker_pricing_response_data_value import MarkerPricingResponseDataValue
+
+
+class MarkerPricingResponse(UniversalBaseModel):
+ """
+ Per-marker pricing, keyed by marker ID.
+ """
+
+ currency: typing.Optional[typing.Literal["USD"]] = None
+ data: typing.Dict[str, MarkerPricingResponseDataValue]
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/junction/types/marker_pricing_response_data_value.py b/src/junction/types/marker_pricing_response_data_value.py
new file mode 100644
index 0000000..f2c4769
--- /dev/null
+++ b/src/junction/types/marker_pricing_response_data_value.py
@@ -0,0 +1,8 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+from .specified_pricing_marker_pricing_conditions import SpecifiedPricingMarkerPricingConditions
+from .unspecified_pricing import UnspecifiedPricing
+
+MarkerPricingResponseDataValue = typing.Union[SpecifiedPricingMarkerPricingConditions, UnspecifiedPricing]
diff --git a/src/junction/types/match_candidate_confidence_level.py b/src/junction/types/match_candidate_confidence_level.py
new file mode 100644
index 0000000..c555799
--- /dev/null
+++ b/src/junction/types/match_candidate_confidence_level.py
@@ -0,0 +1,42 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+from ..core import enum
+
+T_Result = typing.TypeVar("T_Result")
+
+
+class MatchCandidateConfidenceLevel(enum.StrEnum):
+ """
+ ℹ️ This enum is non-exhaustive.
+ """
+
+ HIGH = "high"
+ MEDIUM = "medium"
+ LOW = "low"
+ _UNKNOWN = "__MATCHCANDIDATECONFIDENCELEVEL_UNKNOWN__"
+ """
+ This member is used for forward compatibility. If the value is not recognized by the enum, it will be stored here, and the raw value is accessible through `.value`.
+ """
+
+ @classmethod
+ def _missing_(cls, value: typing.Any) -> "MatchCandidateConfidenceLevel":
+ unknown = cls._UNKNOWN
+ unknown._value_ = value
+ return unknown
+
+ def visit(
+ self,
+ high: typing.Callable[[], T_Result],
+ medium: typing.Callable[[], T_Result],
+ low: typing.Callable[[], T_Result],
+ _unknown_member: typing.Callable[[str], T_Result],
+ ) -> T_Result:
+ if self is MatchCandidateConfidenceLevel.HIGH:
+ return high()
+ if self is MatchCandidateConfidenceLevel.MEDIUM:
+ return medium()
+ if self is MatchCandidateConfidenceLevel.LOW:
+ return low()
+ return _unknown_member(self._value_)
diff --git a/src/junction/types/match_candidate_reason_code.py b/src/junction/types/match_candidate_reason_code.py
new file mode 100644
index 0000000..37852f9
--- /dev/null
+++ b/src/junction/types/match_candidate_reason_code.py
@@ -0,0 +1,82 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+from ..core import enum
+
+T_Result = typing.TypeVar("T_Result")
+
+
+class MatchCandidateReasonCode(enum.StrEnum):
+ """
+ ℹ️ This enum is non-exhaustive.
+ """
+
+ SAMPLE_ID_MATCH = "sample_id_match"
+ LAB_ACCOUNT_MISMATCH = "lab_account_mismatch"
+ MATCHED_COMPLETED_ORDER = "matched_completed_order"
+ MATCHED_CANCELLED_ORDER = "matched_cancelled_order"
+ INVALID_COLLECTION_STATE = "invalid_collection_state"
+ DEMOGRAPHIC_MISMATCH = "demographic_mismatch"
+ DEMOGRAPHIC_MATCH = "demographic_match"
+ POSSIBLE_WRONG_LAB = "possible_wrong_lab"
+ SAME_LAB = "same_lab"
+ SAME_USER = "same_user"
+ MARKER_OVERLAP_EXACT = "marker_overlap_exact"
+ MARKER_OVERLAP_PARTIAL = "marker_overlap_partial"
+ MARKER_OVERLAP_NONE = "marker_overlap_none"
+ _UNKNOWN = "__MATCHCANDIDATEREASONCODE_UNKNOWN__"
+ """
+ This member is used for forward compatibility. If the value is not recognized by the enum, it will be stored here, and the raw value is accessible through `.value`.
+ """
+
+ @classmethod
+ def _missing_(cls, value: typing.Any) -> "MatchCandidateReasonCode":
+ unknown = cls._UNKNOWN
+ unknown._value_ = value
+ return unknown
+
+ def visit(
+ self,
+ sample_id_match: typing.Callable[[], T_Result],
+ lab_account_mismatch: typing.Callable[[], T_Result],
+ matched_completed_order: typing.Callable[[], T_Result],
+ matched_cancelled_order: typing.Callable[[], T_Result],
+ invalid_collection_state: typing.Callable[[], T_Result],
+ demographic_mismatch: typing.Callable[[], T_Result],
+ demographic_match: typing.Callable[[], T_Result],
+ possible_wrong_lab: typing.Callable[[], T_Result],
+ same_lab: typing.Callable[[], T_Result],
+ same_user: typing.Callable[[], T_Result],
+ marker_overlap_exact: typing.Callable[[], T_Result],
+ marker_overlap_partial: typing.Callable[[], T_Result],
+ marker_overlap_none: typing.Callable[[], T_Result],
+ _unknown_member: typing.Callable[[str], T_Result],
+ ) -> T_Result:
+ if self is MatchCandidateReasonCode.SAMPLE_ID_MATCH:
+ return sample_id_match()
+ if self is MatchCandidateReasonCode.LAB_ACCOUNT_MISMATCH:
+ return lab_account_mismatch()
+ if self is MatchCandidateReasonCode.MATCHED_COMPLETED_ORDER:
+ return matched_completed_order()
+ if self is MatchCandidateReasonCode.MATCHED_CANCELLED_ORDER:
+ return matched_cancelled_order()
+ if self is MatchCandidateReasonCode.INVALID_COLLECTION_STATE:
+ return invalid_collection_state()
+ if self is MatchCandidateReasonCode.DEMOGRAPHIC_MISMATCH:
+ return demographic_mismatch()
+ if self is MatchCandidateReasonCode.DEMOGRAPHIC_MATCH:
+ return demographic_match()
+ if self is MatchCandidateReasonCode.POSSIBLE_WRONG_LAB:
+ return possible_wrong_lab()
+ if self is MatchCandidateReasonCode.SAME_LAB:
+ return same_lab()
+ if self is MatchCandidateReasonCode.SAME_USER:
+ return same_user()
+ if self is MatchCandidateReasonCode.MARKER_OVERLAP_EXACT:
+ return marker_overlap_exact()
+ if self is MatchCandidateReasonCode.MARKER_OVERLAP_PARTIAL:
+ return marker_overlap_partial()
+ if self is MatchCandidateReasonCode.MARKER_OVERLAP_NONE:
+ return marker_overlap_none()
+ return _unknown_member(self._value_)
diff --git a/src/junction/types/match_candidate_type.py b/src/junction/types/match_candidate_type.py
new file mode 100644
index 0000000..7d74dd3
--- /dev/null
+++ b/src/junction/types/match_candidate_type.py
@@ -0,0 +1,38 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+from ..core import enum
+
+T_Result = typing.TypeVar("T_Result")
+
+
+class MatchCandidateType(enum.StrEnum):
+ """
+ ℹ️ This enum is non-exhaustive.
+ """
+
+ PROVENANCE_ORDER = "provenance_order"
+ ORDER_OPTION = "order_option"
+ _UNKNOWN = "__MATCHCANDIDATETYPE_UNKNOWN__"
+ """
+ This member is used for forward compatibility. If the value is not recognized by the enum, it will be stored here, and the raw value is accessible through `.value`.
+ """
+
+ @classmethod
+ def _missing_(cls, value: typing.Any) -> "MatchCandidateType":
+ unknown = cls._UNKNOWN
+ unknown._value_ = value
+ return unknown
+
+ def visit(
+ self,
+ provenance_order: typing.Callable[[], T_Result],
+ order_option: typing.Callable[[], T_Result],
+ _unknown_member: typing.Callable[[str], T_Result],
+ ) -> T_Result:
+ if self is MatchCandidateType.PROVENANCE_ORDER:
+ return provenance_order()
+ if self is MatchCandidateType.ORDER_OPTION:
+ return order_option()
+ return _unknown_member(self._value_)
diff --git a/src/junction/types/match_decision_code.py b/src/junction/types/match_decision_code.py
new file mode 100644
index 0000000..22056a3
--- /dev/null
+++ b/src/junction/types/match_decision_code.py
@@ -0,0 +1,66 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+from ..core import enum
+
+T_Result = typing.TypeVar("T_Result")
+
+
+class MatchDecisionCode(enum.StrEnum):
+ """
+ ℹ️ This enum is non-exhaustive.
+ """
+
+ MATCH_SAMPLE_ID = "match_sample_id"
+ MATCH_COMPLETED = "match_completed"
+ MATCH_CANCELLED = "match_cancelled"
+ WRONG_COLLECTION = "wrong_collection"
+ WRONG_LAB = "wrong_lab"
+ MULTIPLE_DEMO_MATCH = "multiple_demo_match"
+ MATCH_SAMPLE_ID_MISMATCH_DEMO = "match_sample_id_mismatch_demo"
+ MATCH_DEMO = "match_demo"
+ NO_MATCH = "no_match"
+ _UNKNOWN = "__MATCHDECISIONCODE_UNKNOWN__"
+ """
+ This member is used for forward compatibility. If the value is not recognized by the enum, it will be stored here, and the raw value is accessible through `.value`.
+ """
+
+ @classmethod
+ def _missing_(cls, value: typing.Any) -> "MatchDecisionCode":
+ unknown = cls._UNKNOWN
+ unknown._value_ = value
+ return unknown
+
+ def visit(
+ self,
+ match_sample_id: typing.Callable[[], T_Result],
+ match_completed: typing.Callable[[], T_Result],
+ match_cancelled: typing.Callable[[], T_Result],
+ wrong_collection: typing.Callable[[], T_Result],
+ wrong_lab: typing.Callable[[], T_Result],
+ multiple_demo_match: typing.Callable[[], T_Result],
+ match_sample_id_mismatch_demo: typing.Callable[[], T_Result],
+ match_demo: typing.Callable[[], T_Result],
+ no_match: typing.Callable[[], T_Result],
+ _unknown_member: typing.Callable[[str], T_Result],
+ ) -> T_Result:
+ if self is MatchDecisionCode.MATCH_SAMPLE_ID:
+ return match_sample_id()
+ if self is MatchDecisionCode.MATCH_COMPLETED:
+ return match_completed()
+ if self is MatchDecisionCode.MATCH_CANCELLED:
+ return match_cancelled()
+ if self is MatchDecisionCode.WRONG_COLLECTION:
+ return wrong_collection()
+ if self is MatchDecisionCode.WRONG_LAB:
+ return wrong_lab()
+ if self is MatchDecisionCode.MULTIPLE_DEMO_MATCH:
+ return multiple_demo_match()
+ if self is MatchDecisionCode.MATCH_SAMPLE_ID_MISMATCH_DEMO:
+ return match_sample_id_mismatch_demo()
+ if self is MatchDecisionCode.MATCH_DEMO:
+ return match_demo()
+ if self is MatchDecisionCode.NO_MATCH:
+ return no_match()
+ return _unknown_member(self._value_)
diff --git a/src/junction/types/match_review_candidate.py b/src/junction/types/match_review_candidate.py
new file mode 100644
index 0000000..7645df5
--- /dev/null
+++ b/src/junction/types/match_review_candidate.py
@@ -0,0 +1,43 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel
+from .match_candidate_confidence_level import MatchCandidateConfidenceLevel
+from .match_candidate_reason_code import MatchCandidateReasonCode
+from .match_candidate_type import MatchCandidateType
+from .order_low_level_status import OrderLowLevelStatus
+
+
+class MatchReviewCandidate(UniversalBaseModel):
+ candidate_type: MatchCandidateType = pydantic.Field()
+ """
+ ℹ️ This enum is non-exhaustive.
+ """
+
+ order_id: typing.Optional[str] = None
+ order_transaction_id: typing.Optional[str] = None
+ last_status: typing.Optional[OrderLowLevelStatus] = pydantic.Field(default=None)
+ """
+ ℹ️ This enum is non-exhaustive.
+ """
+
+ confidence_level: MatchCandidateConfidenceLevel = pydantic.Field()
+ """
+ ℹ️ This enum is non-exhaustive.
+ """
+
+ confidence_score: typing.Optional[float] = None
+ reasons: typing.Optional[typing.List[MatchCandidateReasonCode]] = None
+ marker_overlap: typing.Optional[typing.Dict[str, typing.Any]] = None
+ status_context: typing.Optional[typing.Dict[str, typing.Any]] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/junction/types/match_review_candidate_group.py b/src/junction/types/match_review_candidate_group.py
new file mode 100644
index 0000000..c224144
--- /dev/null
+++ b/src/junction/types/match_review_candidate_group.py
@@ -0,0 +1,21 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel
+from .match_review_candidate import MatchReviewCandidate
+
+
+class MatchReviewCandidateGroup(UniversalBaseModel):
+ user_id: typing.Optional[str] = None
+ candidates: typing.Optional[typing.List[MatchReviewCandidate]] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/junction/types/match_review_lab.py b/src/junction/types/match_review_lab.py
new file mode 100644
index 0000000..34b4653
--- /dev/null
+++ b/src/junction/types/match_review_lab.py
@@ -0,0 +1,21 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel
+
+
+class MatchReviewLab(UniversalBaseModel):
+ id: int
+ name: str
+ slug: str
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/junction/types/match_review_marker.py b/src/junction/types/match_review_marker.py
new file mode 100644
index 0000000..d6a64a6
--- /dev/null
+++ b/src/junction/types/match_review_marker.py
@@ -0,0 +1,20 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel
+
+
+class MatchReviewMarker(UniversalBaseModel):
+ provider_id: typing.Optional[str] = None
+ name: typing.Optional[str] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/junction/types/match_review_patient.py b/src/junction/types/match_review_patient.py
new file mode 100644
index 0000000..c4aade8
--- /dev/null
+++ b/src/junction/types/match_review_patient.py
@@ -0,0 +1,23 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import datetime as dt
+import typing
+
+import pydantic
+from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel
+
+
+class MatchReviewPatient(UniversalBaseModel):
+ first_name: typing.Optional[str] = None
+ last_name: typing.Optional[str] = None
+ dob: typing.Optional[dt.datetime] = None
+ gender: typing.Optional[str] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/junction/types/match_review_resolution_action.py b/src/junction/types/match_review_resolution_action.py
new file mode 100644
index 0000000..4bace5f
--- /dev/null
+++ b/src/junction/types/match_review_resolution_action.py
@@ -0,0 +1,42 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+from ..core import enum
+
+T_Result = typing.TypeVar("T_Result")
+
+
+class MatchReviewResolutionAction(enum.StrEnum):
+ """
+ ℹ️ This enum is non-exhaustive.
+ """
+
+ ACCEPT = "accept"
+ REJECT = "reject"
+ UNSURE = "unsure"
+ _UNKNOWN = "__MATCHREVIEWRESOLUTIONACTION_UNKNOWN__"
+ """
+ This member is used for forward compatibility. If the value is not recognized by the enum, it will be stored here, and the raw value is accessible through `.value`.
+ """
+
+ @classmethod
+ def _missing_(cls, value: typing.Any) -> "MatchReviewResolutionAction":
+ unknown = cls._UNKNOWN
+ unknown._value_ = value
+ return unknown
+
+ def visit(
+ self,
+ accept: typing.Callable[[], T_Result],
+ reject: typing.Callable[[], T_Result],
+ unsure: typing.Callable[[], T_Result],
+ _unknown_member: typing.Callable[[str], T_Result],
+ ) -> T_Result:
+ if self is MatchReviewResolutionAction.ACCEPT:
+ return accept()
+ if self is MatchReviewResolutionAction.REJECT:
+ return reject()
+ if self is MatchReviewResolutionAction.UNSURE:
+ return unsure()
+ return _unknown_member(self._value_)
diff --git a/src/junction/types/match_review_status.py b/src/junction/types/match_review_status.py
new file mode 100644
index 0000000..61b3df8
--- /dev/null
+++ b/src/junction/types/match_review_status.py
@@ -0,0 +1,46 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+from ..core import enum
+
+T_Result = typing.TypeVar("T_Result")
+
+
+class MatchReviewStatus(enum.StrEnum):
+ """
+ ℹ️ This enum is non-exhaustive.
+ """
+
+ MATCHED = "matched"
+ PENDING_CUSTOMER_REVIEW = "pending_customer_review"
+ PENDING_OPS_REVIEW = "pending_ops_review"
+ RESOLVED = "resolved"
+ _UNKNOWN = "__MATCHREVIEWSTATUS_UNKNOWN__"
+ """
+ This member is used for forward compatibility. If the value is not recognized by the enum, it will be stored here, and the raw value is accessible through `.value`.
+ """
+
+ @classmethod
+ def _missing_(cls, value: typing.Any) -> "MatchReviewStatus":
+ unknown = cls._UNKNOWN
+ unknown._value_ = value
+ return unknown
+
+ def visit(
+ self,
+ matched: typing.Callable[[], T_Result],
+ pending_customer_review: typing.Callable[[], T_Result],
+ pending_ops_review: typing.Callable[[], T_Result],
+ resolved: typing.Callable[[], T_Result],
+ _unknown_member: typing.Callable[[str], T_Result],
+ ) -> T_Result:
+ if self is MatchReviewStatus.MATCHED:
+ return matched()
+ if self is MatchReviewStatus.PENDING_CUSTOMER_REVIEW:
+ return pending_customer_review()
+ if self is MatchReviewStatus.PENDING_OPS_REVIEW:
+ return pending_ops_review()
+ if self is MatchReviewStatus.RESOLVED:
+ return resolved()
+ return _unknown_member(self._value_)
diff --git a/src/junction/types/match_review_status_filter.py b/src/junction/types/match_review_status_filter.py
new file mode 100644
index 0000000..283438f
--- /dev/null
+++ b/src/junction/types/match_review_status_filter.py
@@ -0,0 +1,42 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+from ..core import enum
+
+T_Result = typing.TypeVar("T_Result")
+
+
+class MatchReviewStatusFilter(enum.StrEnum):
+ """
+ ℹ️ This enum is non-exhaustive.
+ """
+
+ PENDING_CUSTOMER_REVIEW = "pending_customer_review"
+ PENDING_OPS_REVIEW = "pending_ops_review"
+ RESOLVED = "resolved"
+ _UNKNOWN = "__MATCHREVIEWSTATUSFILTER_UNKNOWN__"
+ """
+ This member is used for forward compatibility. If the value is not recognized by the enum, it will be stored here, and the raw value is accessible through `.value`.
+ """
+
+ @classmethod
+ def _missing_(cls, value: typing.Any) -> "MatchReviewStatusFilter":
+ unknown = cls._UNKNOWN
+ unknown._value_ = value
+ return unknown
+
+ def visit(
+ self,
+ pending_customer_review: typing.Callable[[], T_Result],
+ pending_ops_review: typing.Callable[[], T_Result],
+ resolved: typing.Callable[[], T_Result],
+ _unknown_member: typing.Callable[[str], T_Result],
+ ) -> T_Result:
+ if self is MatchReviewStatusFilter.PENDING_CUSTOMER_REVIEW:
+ return pending_customer_review()
+ if self is MatchReviewStatusFilter.PENDING_OPS_REVIEW:
+ return pending_ops_review()
+ if self is MatchReviewStatusFilter.RESOLVED:
+ return resolved()
+ return _unknown_member(self._value_)
diff --git a/src/junction/types/match_review_webhook_payload.py b/src/junction/types/match_review_webhook_payload.py
new file mode 100644
index 0000000..a65438d
--- /dev/null
+++ b/src/junction/types/match_review_webhook_payload.py
@@ -0,0 +1,41 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel
+from .match_decision_code import MatchDecisionCode
+from .match_review_resolution_action import MatchReviewResolutionAction
+from .match_review_status import MatchReviewStatus
+from .match_sub_reason_code import MatchSubReasonCode
+
+
+class MatchReviewWebhookPayload(UniversalBaseModel):
+ id: str
+ status: MatchReviewStatus = pydantic.Field()
+ """
+ ℹ️ This enum is non-exhaustive.
+ """
+
+ decision_code: MatchDecisionCode = pydantic.Field()
+ """
+ ℹ️ This enum is non-exhaustive.
+ """
+
+ sub_reason_codes: typing.Optional[typing.List[MatchSubReasonCode]] = None
+ resolution_action: typing.Optional[MatchReviewResolutionAction] = pydantic.Field(default=None)
+ """
+ ℹ️ This enum is non-exhaustive.
+ """
+
+ resolved_user_id: typing.Optional[str] = None
+ resolved_order_id: typing.Optional[str] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/junction/types/match_sub_reason_code.py b/src/junction/types/match_sub_reason_code.py
new file mode 100644
index 0000000..99a7f18
--- /dev/null
+++ b/src/junction/types/match_sub_reason_code.py
@@ -0,0 +1,8 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+MatchSubReasonCode = typing.Literal["lab_account_mismatch"]
+"""
+ℹ️ This enum is non-exhaustive.
+"""
diff --git a/src/junction/types/micros.py b/src/junction/types/micros.py
index 2705c39..423dc3f 100644
--- a/src/junction/types/micros.py
+++ b/src/junction/types/micros.py
@@ -9,7 +9,7 @@
class Micros(UniversalBaseModel):
minerals: typing.Optional[typing.Dict[str, typing.Optional[float]]] = pydantic.Field(default=None)
"""
- Amount of each mineral in their respective units. Most minerals are measured in mg (milligrams), while sodium and potassium are in g (grams).
+ Amount of each mineral in milligrams (mg).
"""
trace_elements: typing.Optional[typing.Dict[str, typing.Optional[float]]] = pydantic.Field(default=None)
@@ -19,7 +19,7 @@ class Micros(UniversalBaseModel):
vitamins: typing.Optional[typing.Dict[str, typing.Optional[float]]] = pydantic.Field(default=None)
"""
- Amount of each vitamin in their respective units. A, B12, D and K in μg (micrograms), while B1, B2, B3, B5, B6, C and folic acid are in mg (milligrams).
+ Amount of each vitamin in their respective units. A, B12, D, K, biotin and folic acid are in μg (micrograms), while B1, B2, B3, B5, B6, C and E are in mg (milligrams).
"""
if IS_PYDANTIC_V2:
diff --git a/src/junction/types/o_auth_providers.py b/src/junction/types/o_auth_providers.py
index 558a73d..ea0f493 100644
--- a/src/junction/types/o_auth_providers.py
+++ b/src/junction/types/o_auth_providers.py
@@ -28,6 +28,7 @@ class OAuthProviders(enum.StrEnum):
MY_FITNESS_PAL_V2 = "my_fitness_pal_v2"
ULTRAHUMAN = "ultrahuman"
RUNKEEPER = "runkeeper"
+ GOOGLE_HEALTH = "google_health"
_UNKNOWN = "__OAUTHPROVIDERS_UNKNOWN__"
"""
This member is used for forward compatibility. If the value is not recognized by the enum, it will be stored here, and the raw value is accessible through `.value`.
@@ -57,6 +58,7 @@ def visit(
my_fitness_pal_v2: typing.Callable[[], T_Result],
ultrahuman: typing.Callable[[], T_Result],
runkeeper: typing.Callable[[], T_Result],
+ google_health: typing.Callable[[], T_Result],
_unknown_member: typing.Callable[[str], T_Result],
) -> T_Result:
if self is OAuthProviders.OURA:
@@ -91,4 +93,6 @@ def visit(
return ultrahuman()
if self is OAuthProviders.RUNKEEPER:
return runkeeper()
+ if self is OAuthProviders.GOOGLE_HEALTH:
+ return google_health()
return _unknown_member(self._value_)
diff --git a/src/junction/types/order_status.py b/src/junction/types/order_status.py
index 05b7e7f..93733bd 100644
--- a/src/junction/types/order_status.py
+++ b/src/junction/types/order_status.py
@@ -9,8 +9,7 @@
class OrderStatus(enum.StrEnum):
"""
- Used in combination with OrderStatusDetail to represent the current order state.
- OrderStatus is driven by a FSM whereas OrderStatusDetail is descriptive only. ℹ️ This enum is non-exhaustive.
+ ℹ️ This enum is non-exhaustive.
"""
RECEIVED_WALK_IN_TEST_ORDERED = "received.walk_in_test.ordered"
diff --git a/src/junction/types/parsing_job_failure_reason.py b/src/junction/types/parsing_job_failure_reason.py
index d1c149d..7185d0b 100644
--- a/src/junction/types/parsing_job_failure_reason.py
+++ b/src/junction/types/parsing_job_failure_reason.py
@@ -16,6 +16,7 @@ class ParsingJobFailureReason(enum.StrEnum):
LOW_QUALITY = "low_quality"
NOT_ENGLISH = "not_english"
TOO_MANY_PAGES = "too_many_pages"
+ PROCESSING_ERROR = "processing_error"
_UNKNOWN = "__PARSINGJOBFAILUREREASON_UNKNOWN__"
"""
This member is used for forward compatibility. If the value is not recognized by the enum, it will be stored here, and the raw value is accessible through `.value`.
@@ -33,6 +34,7 @@ def visit(
low_quality: typing.Callable[[], T_Result],
not_english: typing.Callable[[], T_Result],
too_many_pages: typing.Callable[[], T_Result],
+ processing_error: typing.Callable[[], T_Result],
_unknown_member: typing.Callable[[str], T_Result],
) -> T_Result:
if self is ParsingJobFailureReason.INVALID_INPUT:
@@ -43,4 +45,6 @@ def visit(
return not_english()
if self is ParsingJobFailureReason.TOO_MANY_PAGES:
return too_many_pages()
+ if self is ParsingJobFailureReason.PROCESSING_ERROR:
+ return processing_error()
return _unknown_member(self._value_)
diff --git a/src/junction/types/pricing_modifier_marker_pricing_conditions.py b/src/junction/types/pricing_modifier_marker_pricing_conditions.py
new file mode 100644
index 0000000..631501c
--- /dev/null
+++ b/src/junction/types/pricing_modifier_marker_pricing_conditions.py
@@ -0,0 +1,28 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel
+from .marker_pricing_conditions import MarkerPricingConditions
+from .pricing_modifier_marker_pricing_conditions_delta_amount_minor import (
+ PricingModifierMarkerPricingConditionsDeltaAmountMinor,
+)
+
+
+class PricingModifierMarkerPricingConditions(UniversalBaseModel):
+ delta_amount_minor: PricingModifierMarkerPricingConditionsDeltaAmountMinor = pydantic.Field()
+ """
+ Amount delta in the smallest denomination of the currency, e.g. cents for USD.
+ """
+
+ conditions: MarkerPricingConditions
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/junction/types/pricing_modifier_marker_pricing_conditions_delta_amount_minor.py b/src/junction/types/pricing_modifier_marker_pricing_conditions_delta_amount_minor.py
new file mode 100644
index 0000000..d644980
--- /dev/null
+++ b/src/junction/types/pricing_modifier_marker_pricing_conditions_delta_amount_minor.py
@@ -0,0 +1,7 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+from .pricing_modifier_range import PricingModifierRange
+
+PricingModifierMarkerPricingConditionsDeltaAmountMinor = typing.Union[int, PricingModifierRange]
diff --git a/src/junction/types/pricing_modifier_range.py b/src/junction/types/pricing_modifier_range.py
new file mode 100644
index 0000000..2d4cc68
--- /dev/null
+++ b/src/junction/types/pricing_modifier_range.py
@@ -0,0 +1,20 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel
+
+
+class PricingModifierRange(UniversalBaseModel):
+ minimum: int
+ maximum: int
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/junction/types/providers.py b/src/junction/types/providers.py
index 0be2271..4527410 100644
--- a/src/junction/types/providers.py
+++ b/src/junction/types/providers.py
@@ -52,6 +52,7 @@ class Providers(enum.StrEnum):
MY_FITNESS_PAL_V2 = "my_fitness_pal_v2"
MAP_MY_FITNESS = "map_my_fitness"
RUNKEEPER = "runkeeper"
+ GOOGLE_HEALTH = "google_health"
_UNKNOWN = "__PROVIDERS_UNKNOWN__"
"""
This member is used for forward compatibility. If the value is not recognized by the enum, it will be stored here, and the raw value is accessible through `.value`.
@@ -105,6 +106,7 @@ def visit(
my_fitness_pal_v2: typing.Callable[[], T_Result],
map_my_fitness: typing.Callable[[], T_Result],
runkeeper: typing.Callable[[], T_Result],
+ google_health: typing.Callable[[], T_Result],
_unknown_member: typing.Callable[[str], T_Result],
) -> T_Result:
if self is Providers.OURA:
@@ -187,4 +189,6 @@ def visit(
return map_my_fitness()
if self is Providers.RUNKEEPER:
return runkeeper()
+ if self is Providers.GOOGLE_HEALTH:
+ return google_health()
return _unknown_member(self._value_)
diff --git a/src/junction/types/reflex_pricing_condition.py b/src/junction/types/reflex_pricing_condition.py
new file mode 100644
index 0000000..ca9d7c6
--- /dev/null
+++ b/src/junction/types/reflex_pricing_condition.py
@@ -0,0 +1,17 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel
+
+
+class ReflexPricingCondition(UniversalBaseModel):
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/junction/types/result_status.py b/src/junction/types/result_status.py
new file mode 100644
index 0000000..9cb8584
--- /dev/null
+++ b/src/junction/types/result_status.py
@@ -0,0 +1,38 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+from ..core import enum
+
+T_Result = typing.TypeVar("T_Result")
+
+
+class ResultStatus(enum.StrEnum):
+ """
+ ℹ️ This enum is non-exhaustive.
+ """
+
+ FINAL = "final"
+ PARTIAL = "partial"
+ _UNKNOWN = "__RESULTSTATUS_UNKNOWN__"
+ """
+ This member is used for forward compatibility. If the value is not recognized by the enum, it will be stored here, and the raw value is accessible through `.value`.
+ """
+
+ @classmethod
+ def _missing_(cls, value: typing.Any) -> "ResultStatus":
+ unknown = cls._UNKNOWN
+ unknown._value_ = value
+ return unknown
+
+ def visit(
+ self,
+ final: typing.Callable[[], T_Result],
+ partial: typing.Callable[[], T_Result],
+ _unknown_member: typing.Callable[[str], T_Result],
+ ) -> T_Result:
+ if self is ResultStatus.FINAL:
+ return final()
+ if self is ResultStatus.PARTIAL:
+ return partial()
+ return _unknown_member(self._value_)
diff --git a/src/junction/types/search_orderable_tests_response.py b/src/junction/types/search_orderable_tests_response.py
new file mode 100644
index 0000000..3c0577c
--- /dev/null
+++ b/src/junction/types/search_orderable_tests_response.py
@@ -0,0 +1,20 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel
+from .per_lab_candidate import PerLabCandidate
+
+
+class SearchOrderableTestsResponse(UniversalBaseModel):
+ candidates: typing.Optional[typing.Dict[str, typing.List[PerLabCandidate]]] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/junction/types/specified_pricing_marker_pricing_conditions.py b/src/junction/types/specified_pricing_marker_pricing_conditions.py
new file mode 100644
index 0000000..0a6b5c3
--- /dev/null
+++ b/src/junction/types/specified_pricing_marker_pricing_conditions.py
@@ -0,0 +1,25 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel
+from .pricing_modifier_marker_pricing_conditions import PricingModifierMarkerPricingConditions
+
+
+class SpecifiedPricingMarkerPricingConditions(UniversalBaseModel):
+ base_amount_minor: int = pydantic.Field()
+ """
+ Amount chargeable in the smallest denomination of the currency, e.g. cents for USD.
+ """
+
+ modifiers: typing.Optional[typing.List[PricingModifierMarkerPricingConditions]] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/junction/types/unmatched_result.py b/src/junction/types/unmatched_result.py
new file mode 100644
index 0000000..2377a55
--- /dev/null
+++ b/src/junction/types/unmatched_result.py
@@ -0,0 +1,68 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import datetime as dt
+import typing
+
+import pydantic
+from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel
+from .interpretation import Interpretation
+from .match_decision_code import MatchDecisionCode
+from .match_review_candidate_group import MatchReviewCandidateGroup
+from .match_review_lab import MatchReviewLab
+from .match_review_marker import MatchReviewMarker
+from .match_review_patient import MatchReviewPatient
+from .match_review_resolution_action import MatchReviewResolutionAction
+from .match_review_status import MatchReviewStatus
+from .match_sub_reason_code import MatchSubReasonCode
+from .result_status import ResultStatus
+
+
+class UnmatchedResult(UniversalBaseModel):
+ id: str
+ status: MatchReviewStatus = pydantic.Field()
+ """
+ ℹ️ This enum is non-exhaustive.
+ """
+
+ decision_code: MatchDecisionCode = pydantic.Field()
+ """
+ ℹ️ This enum is non-exhaustive.
+ """
+
+ sub_reason_codes: typing.Optional[typing.List[MatchSubReasonCode]] = None
+ reason: str
+ patient: typing.Optional[MatchReviewPatient] = None
+ lab: MatchReviewLab
+ markers: typing.Optional[typing.List[MatchReviewMarker]] = None
+ interpretation: typing.Optional[Interpretation] = pydantic.Field(default=None)
+ """
+ ℹ️ This enum is non-exhaustive.
+ """
+
+ result_status: typing.Optional[ResultStatus] = pydantic.Field(default=None)
+ """
+ ℹ️ This enum is non-exhaustive.
+ """
+
+ note: typing.Optional[str] = None
+ resolution_action: typing.Optional[MatchReviewResolutionAction] = pydantic.Field(default=None)
+ """
+ ℹ️ This enum is non-exhaustive.
+ """
+
+ resolved_user_id: typing.Optional[str] = None
+ resolved_order_id: typing.Optional[str] = None
+ allowed_actions: typing.Optional[typing.List[MatchReviewResolutionAction]] = None
+ candidate_groups: typing.Optional[typing.List[MatchReviewCandidateGroup]] = None
+ created_at: dt.datetime
+ updated_at: dt.datetime
+ reviewed_at: typing.Optional[dt.datetime] = None
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/junction/types/unmatched_result_resolution_action.py b/src/junction/types/unmatched_result_resolution_action.py
new file mode 100644
index 0000000..ed353f4
--- /dev/null
+++ b/src/junction/types/unmatched_result_resolution_action.py
@@ -0,0 +1,38 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+from ..core import enum
+
+T_Result = typing.TypeVar("T_Result")
+
+
+class UnmatchedResultResolutionAction(enum.StrEnum):
+ """
+ ℹ️ This enum is non-exhaustive.
+ """
+
+ REJECT = "reject"
+ UNSURE = "unsure"
+ _UNKNOWN = "__UNMATCHEDRESULTRESOLUTIONACTION_UNKNOWN__"
+ """
+ This member is used for forward compatibility. If the value is not recognized by the enum, it will be stored here, and the raw value is accessible through `.value`.
+ """
+
+ @classmethod
+ def _missing_(cls, value: typing.Any) -> "UnmatchedResultResolutionAction":
+ unknown = cls._UNKNOWN
+ unknown._value_ = value
+ return unknown
+
+ def visit(
+ self,
+ reject: typing.Callable[[], T_Result],
+ unsure: typing.Callable[[], T_Result],
+ _unknown_member: typing.Callable[[str], T_Result],
+ ) -> T_Result:
+ if self is UnmatchedResultResolutionAction.REJECT:
+ return reject()
+ if self is UnmatchedResultResolutionAction.UNSURE:
+ return unsure()
+ return _unknown_member(self._value_)
diff --git a/src/junction/types/unmatched_result_test_case.py b/src/junction/types/unmatched_result_test_case.py
new file mode 100644
index 0000000..3348fe5
--- /dev/null
+++ b/src/junction/types/unmatched_result_test_case.py
@@ -0,0 +1,62 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+from ..core import enum
+
+T_Result = typing.TypeVar("T_Result")
+
+
+class UnmatchedResultTestCase(enum.StrEnum):
+ """
+ ℹ️ This enum is non-exhaustive.
+ """
+
+ MATCH_COMPLETED = "match_completed"
+ MATCH_CANCELLED = "match_cancelled"
+ WRONG_COLLECTION = "wrong_collection"
+ WRONG_LAB = "wrong_lab"
+ MULTIPLE_DEMO_MATCH = "multiple_demo_match"
+ MATCH_SAMPLE_ID_MISMATCH_DEMO = "match_sample_id_mismatch_demo"
+ MATCH_DEMO = "match_demo"
+ NO_MATCH = "no_match"
+ _UNKNOWN = "__UNMATCHEDRESULTTESTCASE_UNKNOWN__"
+ """
+ This member is used for forward compatibility. If the value is not recognized by the enum, it will be stored here, and the raw value is accessible through `.value`.
+ """
+
+ @classmethod
+ def _missing_(cls, value: typing.Any) -> "UnmatchedResultTestCase":
+ unknown = cls._UNKNOWN
+ unknown._value_ = value
+ return unknown
+
+ def visit(
+ self,
+ match_completed: typing.Callable[[], T_Result],
+ match_cancelled: typing.Callable[[], T_Result],
+ wrong_collection: typing.Callable[[], T_Result],
+ wrong_lab: typing.Callable[[], T_Result],
+ multiple_demo_match: typing.Callable[[], T_Result],
+ match_sample_id_mismatch_demo: typing.Callable[[], T_Result],
+ match_demo: typing.Callable[[], T_Result],
+ no_match: typing.Callable[[], T_Result],
+ _unknown_member: typing.Callable[[str], T_Result],
+ ) -> T_Result:
+ if self is UnmatchedResultTestCase.MATCH_COMPLETED:
+ return match_completed()
+ if self is UnmatchedResultTestCase.MATCH_CANCELLED:
+ return match_cancelled()
+ if self is UnmatchedResultTestCase.WRONG_COLLECTION:
+ return wrong_collection()
+ if self is UnmatchedResultTestCase.WRONG_LAB:
+ return wrong_lab()
+ if self is UnmatchedResultTestCase.MULTIPLE_DEMO_MATCH:
+ return multiple_demo_match()
+ if self is UnmatchedResultTestCase.MATCH_SAMPLE_ID_MISMATCH_DEMO:
+ return match_sample_id_mismatch_demo()
+ if self is UnmatchedResultTestCase.MATCH_DEMO:
+ return match_demo()
+ if self is UnmatchedResultTestCase.NO_MATCH:
+ return no_match()
+ return _unknown_member(self._value_)
diff --git a/src/junction/types/unmatched_result_test_case_definition.py b/src/junction/types/unmatched_result_test_case_definition.py
new file mode 100644
index 0000000..41ee9a1
--- /dev/null
+++ b/src/junction/types/unmatched_result_test_case_definition.py
@@ -0,0 +1,28 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel
+from .order_low_level_status import OrderLowLevelStatus
+from .unmatched_result_test_case import UnmatchedResultTestCase
+
+
+class UnmatchedResultTestCaseDefinition(UniversalBaseModel):
+ case: UnmatchedResultTestCase = pydantic.Field()
+ """
+ ℹ️ This enum is non-exhaustive.
+ """
+
+ order_roles: typing.List[str]
+ required_statuses: typing.Dict[str, OrderLowLevelStatus]
+ lab_constraints: typing.List[str]
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/junction/types/unmatched_result_test_failure.py b/src/junction/types/unmatched_result_test_failure.py
new file mode 100644
index 0000000..ea3a8d2
--- /dev/null
+++ b/src/junction/types/unmatched_result_test_failure.py
@@ -0,0 +1,20 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel
+
+
+class UnmatchedResultTestFailure(UniversalBaseModel):
+ code: str
+ message: str
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/junction/types/unmatched_result_test_order_artifact.py b/src/junction/types/unmatched_result_test_order_artifact.py
new file mode 100644
index 0000000..82b708a
--- /dev/null
+++ b/src/junction/types/unmatched_result_test_order_artifact.py
@@ -0,0 +1,21 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel
+
+
+class UnmatchedResultTestOrderArtifact(UniversalBaseModel):
+ order_id: str
+ user_id: str
+ order_transaction_id: str
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/junction/types/unmatched_result_test_order_source.py b/src/junction/types/unmatched_result_test_order_source.py
new file mode 100644
index 0000000..ac00dda
--- /dev/null
+++ b/src/junction/types/unmatched_result_test_order_source.py
@@ -0,0 +1,38 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+from ..core import enum
+
+T_Result = typing.TypeVar("T_Result")
+
+
+class UnmatchedResultTestOrderSource(enum.StrEnum):
+ """
+ ℹ️ This enum is non-exhaustive.
+ """
+
+ MANAGED = "managed"
+ PROVIDED = "provided"
+ _UNKNOWN = "__UNMATCHEDRESULTTESTORDERSOURCE_UNKNOWN__"
+ """
+ This member is used for forward compatibility. If the value is not recognized by the enum, it will be stored here, and the raw value is accessible through `.value`.
+ """
+
+ @classmethod
+ def _missing_(cls, value: typing.Any) -> "UnmatchedResultTestOrderSource":
+ unknown = cls._UNKNOWN
+ unknown._value_ = value
+ return unknown
+
+ def visit(
+ self,
+ managed: typing.Callable[[], T_Result],
+ provided: typing.Callable[[], T_Result],
+ _unknown_member: typing.Callable[[str], T_Result],
+ ) -> T_Result:
+ if self is UnmatchedResultTestOrderSource.MANAGED:
+ return managed()
+ if self is UnmatchedResultTestOrderSource.PROVIDED:
+ return provided()
+ return _unknown_member(self._value_)
diff --git a/src/junction/types/unmatched_result_test_run_status.py b/src/junction/types/unmatched_result_test_run_status.py
new file mode 100644
index 0000000..2f59626
--- /dev/null
+++ b/src/junction/types/unmatched_result_test_run_status.py
@@ -0,0 +1,46 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+from ..core import enum
+
+T_Result = typing.TypeVar("T_Result")
+
+
+class UnmatchedResultTestRunStatus(enum.StrEnum):
+ """
+ ℹ️ This enum is non-exhaustive.
+ """
+
+ QUEUED = "queued"
+ RUNNING = "running"
+ SUCCEEDED = "succeeded"
+ FAILED = "failed"
+ _UNKNOWN = "__UNMATCHEDRESULTTESTRUNSTATUS_UNKNOWN__"
+ """
+ This member is used for forward compatibility. If the value is not recognized by the enum, it will be stored here, and the raw value is accessible through `.value`.
+ """
+
+ @classmethod
+ def _missing_(cls, value: typing.Any) -> "UnmatchedResultTestRunStatus":
+ unknown = cls._UNKNOWN
+ unknown._value_ = value
+ return unknown
+
+ def visit(
+ self,
+ queued: typing.Callable[[], T_Result],
+ running: typing.Callable[[], T_Result],
+ succeeded: typing.Callable[[], T_Result],
+ failed: typing.Callable[[], T_Result],
+ _unknown_member: typing.Callable[[str], T_Result],
+ ) -> T_Result:
+ if self is UnmatchedResultTestRunStatus.QUEUED:
+ return queued()
+ if self is UnmatchedResultTestRunStatus.RUNNING:
+ return running()
+ if self is UnmatchedResultTestRunStatus.SUCCEEDED:
+ return succeeded()
+ if self is UnmatchedResultTestRunStatus.FAILED:
+ return failed()
+ return _unknown_member(self._value_)
diff --git a/src/junction/types/unmatched_result_test_stage.py b/src/junction/types/unmatched_result_test_stage.py
new file mode 100644
index 0000000..adeba0f
--- /dev/null
+++ b/src/junction/types/unmatched_result_test_stage.py
@@ -0,0 +1,70 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+from ..core import enum
+
+T_Result = typing.TypeVar("T_Result")
+
+
+class UnmatchedResultTestStage(enum.StrEnum):
+ """
+ ℹ️ This enum is non-exhaustive.
+ """
+
+ QUEUED = "queued"
+ VALIDATING = "validating"
+ SELECTING_CONFIGURATION = "selecting_configuration"
+ CREATING_ORDERS = "creating_orders"
+ TRANSITIONING_ORDERS = "transitioning_orders"
+ WRITING_RESULT = "writing_result"
+ MATCHING_RESULT = "matching_result"
+ VERIFYING_REVIEW = "verifying_review"
+ SUCCEEDED = "succeeded"
+ FAILED = "failed"
+ _UNKNOWN = "__UNMATCHEDRESULTTESTSTAGE_UNKNOWN__"
+ """
+ This member is used for forward compatibility. If the value is not recognized by the enum, it will be stored here, and the raw value is accessible through `.value`.
+ """
+
+ @classmethod
+ def _missing_(cls, value: typing.Any) -> "UnmatchedResultTestStage":
+ unknown = cls._UNKNOWN
+ unknown._value_ = value
+ return unknown
+
+ def visit(
+ self,
+ queued: typing.Callable[[], T_Result],
+ validating: typing.Callable[[], T_Result],
+ selecting_configuration: typing.Callable[[], T_Result],
+ creating_orders: typing.Callable[[], T_Result],
+ transitioning_orders: typing.Callable[[], T_Result],
+ writing_result: typing.Callable[[], T_Result],
+ matching_result: typing.Callable[[], T_Result],
+ verifying_review: typing.Callable[[], T_Result],
+ succeeded: typing.Callable[[], T_Result],
+ failed: typing.Callable[[], T_Result],
+ _unknown_member: typing.Callable[[str], T_Result],
+ ) -> T_Result:
+ if self is UnmatchedResultTestStage.QUEUED:
+ return queued()
+ if self is UnmatchedResultTestStage.VALIDATING:
+ return validating()
+ if self is UnmatchedResultTestStage.SELECTING_CONFIGURATION:
+ return selecting_configuration()
+ if self is UnmatchedResultTestStage.CREATING_ORDERS:
+ return creating_orders()
+ if self is UnmatchedResultTestStage.TRANSITIONING_ORDERS:
+ return transitioning_orders()
+ if self is UnmatchedResultTestStage.WRITING_RESULT:
+ return writing_result()
+ if self is UnmatchedResultTestStage.MATCHING_RESULT:
+ return matching_result()
+ if self is UnmatchedResultTestStage.VERIFYING_REVIEW:
+ return verifying_review()
+ if self is UnmatchedResultTestStage.SUCCEEDED:
+ return succeeded()
+ if self is UnmatchedResultTestStage.FAILED:
+ return failed()
+ return _unknown_member(self._value_)
diff --git a/src/junction/types/unspecified_pricing.py b/src/junction/types/unspecified_pricing.py
new file mode 100644
index 0000000..21a353a
--- /dev/null
+++ b/src/junction/types/unspecified_pricing.py
@@ -0,0 +1,23 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel
+from .unspecified_pricing_unspecified import UnspecifiedPricingUnspecified
+
+
+class UnspecifiedPricing(UniversalBaseModel):
+ unspecified: UnspecifiedPricingUnspecified = pydantic.Field()
+ """
+ ℹ️ This enum is non-exhaustive.
+ """
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/junction/types/unspecified_pricing_unspecified.py b/src/junction/types/unspecified_pricing_unspecified.py
new file mode 100644
index 0000000..b0c75a8
--- /dev/null
+++ b/src/junction/types/unspecified_pricing_unspecified.py
@@ -0,0 +1,38 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+from ..core import enum
+
+T_Result = typing.TypeVar("T_Result")
+
+
+class UnspecifiedPricingUnspecified(enum.StrEnum):
+ """
+ ℹ️ This enum is non-exhaustive.
+ """
+
+ REFER_TO_CONTRACT = "refer_to_contract"
+ PRICE_UNAVAILABLE = "price_unavailable"
+ _UNKNOWN = "__UNSPECIFIEDPRICINGUNSPECIFIED_UNKNOWN__"
+ """
+ This member is used for forward compatibility. If the value is not recognized by the enum, it will be stored here, and the raw value is accessible through `.value`.
+ """
+
+ @classmethod
+ def _missing_(cls, value: typing.Any) -> "UnspecifiedPricingUnspecified":
+ unknown = cls._UNKNOWN
+ unknown._value_ = value
+ return unknown
+
+ def visit(
+ self,
+ refer_to_contract: typing.Callable[[], T_Result],
+ price_unavailable: typing.Callable[[], T_Result],
+ _unknown_member: typing.Callable[[str], T_Result],
+ ) -> T_Result:
+ if self is UnspecifiedPricingUnspecified.REFER_TO_CONTRACT:
+ return refer_to_contract()
+ if self is UnspecifiedPricingUnspecified.PRICE_UNAVAILABLE:
+ return price_unavailable()
+ return _unknown_member(self._value_)
diff --git a/src/junction/types/us_state_pricing_condition.py b/src/junction/types/us_state_pricing_condition.py
new file mode 100644
index 0000000..5487da0
--- /dev/null
+++ b/src/junction/types/us_state_pricing_condition.py
@@ -0,0 +1,19 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel
+
+
+class UsStatePricingCondition(UniversalBaseModel):
+ any_of: typing.List[str]
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/tests/utils/test_http_client.py b/tests/utils/test_http_client.py
index 8c96a0f..c6f7e00 100644
--- a/tests/utils/test_http_client.py
+++ b/tests/utils/test_http_client.py
@@ -1,6 +1,6 @@
# This file was auto-generated by Fern from our API Definition.
-from typing import Any, Dict
+from typing import Any, Dict, Tuple, cast
from unittest.mock import AsyncMock, MagicMock, patch
import httpx
@@ -692,3 +692,70 @@ def test_should_not_retry_non_retryable_status_codes(status_code: int) -> None:
def test_should_retry_599_upper_boundary() -> None:
"""Legacy mode retries on >= 500, which includes 599."""
assert _should_retry(_make_response(599)) is True
+
+
+# ---------------------------------------------------------------------------
+# RequestOptions timeout resolution tests (timeout / deprecated timeout_in_seconds)
+# ---------------------------------------------------------------------------
+
+
+def _sync_client_with_base_timeout(base_timeout: Any) -> Tuple[HttpClient, _DummySyncClient]:
+ dummy_client = _DummySyncClient()
+ http_client = HttpClient(
+ httpx_client=dummy_client, # type: ignore[arg-type]
+ base_timeout=lambda: base_timeout,
+ base_headers=lambda: {},
+ base_url=lambda: "https://example.com",
+ )
+ return http_client, dummy_client
+
+
+def test_sync_request_options_timeout_used() -> None:
+ """The new `timeout` request option is passed through to httpx (in seconds)."""
+ http_client, dummy_client = _sync_client_with_base_timeout(60)
+ http_client.request(path="/test", method="GET", request_options={"timeout": 30})
+ assert dummy_client.last_request_kwargs["timeout"] == 30
+
+
+def test_sync_request_options_timeout_in_seconds_still_works() -> None:
+ """The deprecated `timeout_in_seconds` request option remains backwards compatible."""
+ http_client, dummy_client = _sync_client_with_base_timeout(60)
+ http_client.request(path="/test", method="GET", request_options={"timeout_in_seconds": 45})
+ assert dummy_client.last_request_kwargs["timeout"] == 45
+
+
+def test_sync_request_options_timeout_takes_precedence() -> None:
+ """When both are set, `timeout` wins over `timeout_in_seconds` (same seconds unit)."""
+ http_client, dummy_client = _sync_client_with_base_timeout(60)
+ http_client.request(path="/test", method="GET", request_options={"timeout": 30, "timeout_in_seconds": 45})
+ assert dummy_client.last_request_kwargs["timeout"] == 30
+
+
+def test_sync_request_options_timeout_falls_back_to_base() -> None:
+ """When neither key is set, the client-level base timeout is used."""
+ http_client, dummy_client = _sync_client_with_base_timeout(60)
+ http_client.request(path="/test", method="GET", request_options=None)
+ assert dummy_client.last_request_kwargs["timeout"] == 60
+
+
+def test_sync_request_options_timeout_none_falls_back_to_deprecated() -> None:
+ """An explicit `timeout=None` (dynamic caller) falls back to the deprecated `timeout_in_seconds`."""
+ http_client, dummy_client = _sync_client_with_base_timeout(60)
+ request_options = cast(RequestOptions, {"timeout": None, "timeout_in_seconds": 45})
+ http_client.request(path="/test", method="GET", request_options=request_options)
+ assert dummy_client.last_request_kwargs["timeout"] == 45
+
+
+@pytest.mark.asyncio
+async def test_async_request_options_timeout_takes_precedence() -> None:
+ """Async: `timeout` wins over the deprecated `timeout_in_seconds`."""
+ dummy_client = _DummyAsyncClient()
+ http_client = AsyncHttpClient(
+ httpx_client=dummy_client, # type: ignore[arg-type]
+ base_timeout=lambda: 60,
+ base_headers=lambda: {},
+ base_url=lambda: "https://example.com",
+ async_base_headers=None,
+ )
+ await http_client.request(path="/test", method="GET", request_options={"timeout": 30, "timeout_in_seconds": 45})
+ assert dummy_client.last_request_kwargs["timeout"] == 30